Compare commits

..

2 Commits

Author SHA1 Message Date
Cort Buffington 06514567ef Update hblink.py 2020-10-13 08:33:23 -05:00
Cort Buffington 3ee762475e remove cruft 2020-10-12 06:49:33 -05:00
11 changed files with 312 additions and 251 deletions

View File

@ -1,12 +1,6 @@
# OBP-Master #
--- ---
### Forked from the HBLink project ###
### FOR SUPPORT, DISCUSSION, GETTING INVOLVED ### ### FOR SUPPORT, DISCUSSION, GETTING INVOLVED ###
Please join the DVSwitch group at groups.io for online forum support, discussion, and to become part of the development team. Please join the DVSwitch group at groups.io for online forum support, discussion, and to become part of the development team.
DVSwitch@groups.io DVSwitch@groups.io

View File

@ -1050,10 +1050,8 @@ class routerHBP(HBSYSTEM):
logger.error('(%s) *UNIT CALL NOT FORWARDED* UNIT calling is disabled for this system (INGRESS)', self._system) logger.error('(%s) *UNIT CALL NOT FORWARDED* UNIT calling is disabled for this system (INGRESS)', self._system)
else: else:
self.unit_received(_peer_id, _rf_src, _dst_id, _seq, _slot, _frame_type, _dtype_vseq, _stream_id, _data) self.unit_received(_peer_id, _rf_src, _dst_id, _seq, _slot, _frame_type, _dtype_vseq, _stream_id, _data)
elif _call_type == 'vcsbk': elif _call_type == 'vscsbk':
#logger.debug('CSBK recieved, but HBlink does not process them currently') logger.debug('CSBK recieved, but HBlink does not process them currently')
logger.debug('CSBK recieved, routing to ' + str(int_id(_dst_id)))
self.group_received(_peer_id, _rf_src, _dst_id, _seq, _slot, _frame_type, _dtype_vseq, _stream_id, _data)
else: else:
logger.error('Unknown call type recieved -- not processed') logger.error('Unknown call type recieved -- not processed')

View File

@ -182,7 +182,7 @@ def build_config(_config_file):
'SOFTWARE_ID': bytes(config.get(section, 'SOFTWARE_ID').ljust(40)[:40], 'utf-8'), 'SOFTWARE_ID': bytes(config.get(section, 'SOFTWARE_ID').ljust(40)[:40], 'utf-8'),
'PACKAGE_ID': bytes(config.get(section, 'PACKAGE_ID').ljust(40)[:40], 'utf-8'), 'PACKAGE_ID': bytes(config.get(section, 'PACKAGE_ID').ljust(40)[:40], 'utf-8'),
'GROUP_HANGTIME': config.getint(section, 'GROUP_HANGTIME'), 'GROUP_HANGTIME': config.getint(section, 'GROUP_HANGTIME'),
'OPTIONS': b''.join([b'Type=HBlink;', bytes(config.get(section, 'OPTIONS'), 'utf-8')]), 'OPTIONS': bytes(config.get(section, 'OPTIONS'), 'utf-8'),
'USE_ACL': config.getboolean(section, 'USE_ACL'), 'USE_ACL': config.getboolean(section, 'USE_ACL'),
'SUB_ACL': config.get(section, 'SUB_ACL'), 'SUB_ACL': config.get(section, 'SUB_ACL'),
'TG1_ACL': config.get(section, 'TGID_TS1_ACL'), 'TG1_ACL': config.get(section, 'TGID_TS1_ACL'),
@ -199,52 +199,6 @@ def build_config(_config_file):
'LAST_PING_ACK_TIME': 0, 'LAST_PING_ACK_TIME': 0,
}}) }})
if config.get(section, 'MODE') == 'XLXPEER':
CONFIG['SYSTEMS'].update({section: {
'MODE': config.get(section, 'MODE'),
'ENABLED': config.getboolean(section, 'ENABLED'),
'LOOSE': config.getboolean(section, 'LOOSE'),
'SOCK_ADDR': (gethostbyname(config.get(section, 'IP')), config.getint(section, 'PORT')),
'IP': gethostbyname(config.get(section, 'IP')),
'PORT': config.getint(section, 'PORT'),
'MASTER_SOCKADDR': (gethostbyname(config.get(section, 'MASTER_IP')), config.getint(section, 'MASTER_PORT')),
'MASTER_IP': gethostbyname(config.get(section, 'MASTER_IP')),
'MASTER_PORT': config.getint(section, 'MASTER_PORT'),
'PASSPHRASE': bytes(config.get(section, 'PASSPHRASE'), 'utf-8'),
'CALLSIGN': bytes(config.get(section, 'CALLSIGN').ljust(8)[:8], 'utf-8'),
'RADIO_ID': config.getint(section, 'RADIO_ID').to_bytes(4, 'big'),
'RX_FREQ': bytes(config.get(section, 'RX_FREQ').ljust(9)[:9], 'utf-8'),
'TX_FREQ': bytes(config.get(section, 'TX_FREQ').ljust(9)[:9], 'utf-8'),
'TX_POWER': bytes(config.get(section, 'TX_POWER').rjust(2,'0'), 'utf-8'),
'COLORCODE': bytes(config.get(section, 'COLORCODE').rjust(2,'0'), 'utf-8'),
'LATITUDE': bytes(config.get(section, 'LATITUDE').ljust(8)[:8], 'utf-8'),
'LONGITUDE': bytes(config.get(section, 'LONGITUDE').ljust(9)[:9], 'utf-8'),
'HEIGHT': bytes(config.get(section, 'HEIGHT').rjust(3,'0'), 'utf-8'),
'LOCATION': bytes(config.get(section, 'LOCATION').ljust(20)[:20], 'utf-8'),
'DESCRIPTION': bytes(config.get(section, 'DESCRIPTION').ljust(19)[:19], 'utf-8'),
'SLOTS': bytes(config.get(section, 'SLOTS'), 'utf-8'),
'URL': bytes(config.get(section, 'URL').ljust(124)[:124], 'utf-8'),
'SOFTWARE_ID': bytes(config.get(section, 'SOFTWARE_ID').ljust(40)[:40], 'utf-8'),
'PACKAGE_ID': bytes(config.get(section, 'PACKAGE_ID').ljust(40)[:40], 'utf-8'),
'GROUP_HANGTIME': config.getint(section, 'GROUP_HANGTIME'),
'XLXMODULE': config.getint(section, 'XLXMODULE'),
'OPTIONS': '',
'USE_ACL': config.getboolean(section, 'USE_ACL'),
'SUB_ACL': config.get(section, 'SUB_ACL'),
'TG1_ACL': config.get(section, 'TGID_TS1_ACL'),
'TG2_ACL': config.get(section, 'TGID_TS2_ACL')
}})
CONFIG['SYSTEMS'][section].update({'XLXSTATS': {
'CONNECTION': 'NO', # NO, RTPL_SENT, AUTHENTICATED, CONFIG-SENT, YES
'CONNECTED': None,
'PINGS_SENT': 0,
'PINGS_ACKD': 0,
'NUM_OUTSTANDING': 0,
'PING_OUTSTANDING': False,
'LAST_PING_TX_TIME': 0,
'LAST_PING_ACK_TIME': 0,
}})
elif config.get(section, 'MODE') == 'MASTER': elif config.get(section, 'MODE') == 'MASTER':
CONFIG['SYSTEMS'].update({section: { CONFIG['SYSTEMS'].update({section: {
'MODE': config.get(section, 'MODE'), 'MODE': config.get(section, 'MODE'),

View File

@ -50,7 +50,6 @@ HBPF_SLT_VHEAD = 0x1
HBPF_SLT_VTERM = 0x2 HBPF_SLT_VTERM = 0x2
# HomeBrew Protocol Commands # HomeBrew Protocol Commands
DMRA = b'DMRA'
DMRD = b'DMRD' DMRD = b'DMRD'
MSTCL = b'MSTCL' MSTCL = b'MSTCL'
MSTNAK = b'MSTNAK' MSTNAK = b'MSTNAK'

127
hblink-750.cfg Executable file
View File

@ -0,0 +1,127 @@
[GLOBAL]
PATH: ./
PING_TIME: 5
MAX_MISSED: 3
USE_ACL: False
REG_ACL: DENY:1
SUB_ACL: DENY:1
TGID_TS1_ACL: PERMIT:ALL
TGID_TS2_ACL: PERMIT:ALL
[REPORTS]
REPORT: False
REPORT_INTERVAL: 60
REPORT_PORT: 4321
REPORT_CLIENTS: 127.0.0.1
[LOGGER]
LOG_FILE: /tmp/hblink.log
LOG_HANDLERS: console-timed
LOG_LEVEL: INFO
LOG_NAME: 444.750
[ALIASES]
TRY_DOWNLOAD: False
PATH: ./
PEER_FILE: peer_ids.json
SUBSCRIBER_FILE: subscriber_ids.json
TGID_FILE: talkgroup_ids.json
PEER_URL: https://www.radioid.net/static/rptrs.json
SUBSCRIBER_URL: https://www.radioid.net/static/users.json
STALE_DAYS: 7
[OBP]
MODE: OPENBRIDGE
ENABLED: True
IP:
PORT: 50100
NETWORK_ID: 1
PASSPHRASE: deadbeef
#TARGET_IP: olympic.k0usy.org
TARGET_IP: 127.0.0.1
#TARGET_PORT: 50666
TARGET_PORT: 50101
BOTH_SLOTS: True
USE_ACL: False
SUB_ACL: DENY:1
TGID_ACL: PERMIT:ALL
[444.750]
MODE: MASTER
ENABLED: True
REPEAT: True
MAX_PEERS: 5
EXPORT_AMBE: False
IP:
PORT: 50001
PASSPHRASE: jimmy
GROUP_HANGTIME: 10
USE_ACL: False
REG_ACL: DENY:1
SUB_ACL: DENY:1
TGID_TS1_ACL: DENY:8
TGID_TS2_ACL: PERMIT:3120
[TWO]
MODE: MASTER
ENABLED: False
REPEAT: True
MAX_PEERS: 5
EXPORT_AMBE: False
IP:
PORT:50002
PASSPHRASE: jimmy
GROUP_HANGTIME: 10
USE_ACL: False
REG_ACL: DENY:1
SUB_ACL: DENY:1
TGID_TS1_ACL: DENY:8
TGID_TS2_ACL: PERMIT:3120
[THREE]
MODE: MASTER
ENABLED: False
REPEAT: True
MAX_PEERS: 5
EXPORT_AMBE: False
IP:
PORT:50003
PASSPHRASE: jimmy
GROUP_HANGTIME: 10
USE_ACL: False
REG_ACL: DENY:1
SUB_ACL: DENY:1
TGID_TS1_ACL: DENY:8
TGID_TS2_ACL: PERMIT:3120
[KS-DMR]
MODE: PEER
ENABLED: False
LOOSE: False
EXPORT_AMBE: False
IP:
PORT: 54001
MASTER_IP: olympic.k0usy.org
MASTER_PORT: 62071
PASSPHRASE: c0ffee
CALLSIGN: W1ABC
RADIO_ID: 312312
RX_FREQ: 449000000
TX_FREQ: 444000000
TX_POWER: 25
COLORCODE: 1
SLOTS: 1
LATITUDE: 38.0000
LONGITUDE: -095.0000
HEIGHT: 75
LOCATION: Anywhere, USA
DESCRIPTION: This is a cool repeater
URL: www.w1abc.org
SOFTWARE_ID: 20170620
PACKAGE_ID: MMDVM_HBlink
GROUP_HANGTIME: 5
OPTIONS:
USE_ACL: True
SUB_ACL: DENY:1
TGID_TS1_ACL: PERMIT:ALL
TGID_TS2_ACL: PERMIT:ALL

127
hblink-800.cfg Executable file
View File

@ -0,0 +1,127 @@
[GLOBAL]
PATH: ./
PING_TIME: 5
MAX_MISSED: 3
USE_ACL: False
REG_ACL: DENY:1
SUB_ACL: DENY:1
TGID_TS1_ACL: PERMIT:ALL
TGID_TS2_ACL: PERMIT:ALL
[REPORTS]
REPORT: False
REPORT_INTERVAL: 60
REPORT_PORT: 4321
REPORT_CLIENTS: 127.0.0.1
[LOGGER]
LOG_FILE: /tmp/hblink.log
LOG_HANDLERS: console-timed
LOG_LEVEL: INFO
LOG_NAME: 444.800
[ALIASES]
TRY_DOWNLOAD: False
PATH: ./
PEER_FILE: peer_ids.json
SUBSCRIBER_FILE: subscriber_ids.json
TGID_FILE: talkgroup_ids.json
PEER_URL: https://www.radioid.net/static/rptrs.json
SUBSCRIBER_URL: https://www.radioid.net/static/users.json
STALE_DAYS: 7
[OBP]
MODE: OPENBRIDGE
ENABLED: True
IP:
PORT: 50101
NETWORK_ID: 2
PASSPHRASE: deadbeef
#TARGET_IP: olympic.k0usy.org
TARGET_IP: 127.0.0.1
#TARGET_PORT: 50666
TARGET_PORT: 50100
BOTH_SLOTS: True
USE_ACL: False
SUB_ACL: DENY:1
TGID_ACL: PERMIT:ALL
[444.800]
MODE: MASTER
ENABLED: True
REPEAT: True
MAX_PEERS: 5
EXPORT_AMBE: False
IP:
PORT: 50011
PASSPHRASE: jimmy
GROUP_HANGTIME: 10
USE_ACL: False
REG_ACL: DENY:1
SUB_ACL: DENY:1
TGID_TS1_ACL: DENY:8
TGID_TS2_ACL: PERMIT:3120
[TWO]
MODE: MASTER
ENABLED: False
REPEAT: True
MAX_PEERS: 5
EXPORT_AMBE: False
IP:
PORT:50012
PASSPHRASE: jimmy
GROUP_HANGTIME: 10
USE_ACL: False
REG_ACL: DENY:1
SUB_ACL: DENY:1
TGID_TS1_ACL: DENY:8
TGID_TS2_ACL: PERMIT:3120
[THREE]
MODE: MASTER
ENABLED: False
REPEAT: True
MAX_PEERS: 5
EXPORT_AMBE: False
IP:
PORT:50013
PASSPHRASE: jimmy
GROUP_HANGTIME: 10
USE_ACL: False
REG_ACL: DENY:1
SUB_ACL: DENY:1
TGID_TS1_ACL: DENY:8
TGID_TS2_ACL: PERMIT:3120
[KS-DMR]
MODE: PEER
ENABLED: False
LOOSE: False
EXPORT_AMBE: False
IP:
PORT: 54011
MASTER_IP: olympic.k0usy.org
MASTER_PORT: 62071
PASSPHRASE: c0ffee
CALLSIGN: W1ABC
RADIO_ID: 312312
RX_FREQ: 449000000
TX_FREQ: 444000000
TX_POWER: 25
COLORCODE: 1
SLOTS: 1
LATITUDE: 38.0000
LONGITUDE: -095.0000
HEIGHT: 75
LOCATION: Anywhere, USA
DESCRIPTION: This is a cool repeater
URL: www.w1abc.org
SOFTWARE_ID: 20170620
PACKAGE_ID: MMDVM_HBlink
GROUP_HANGTIME: 5
OPTIONS:
USE_ACL: True
SUB_ACL: DENY:1
TGID_TS1_ACL: PERMIT:ALL
TGID_TS2_ACL: PERMIT:ALL

View File

@ -209,36 +209,4 @@ OPTIONS:
USE_ACL: True USE_ACL: True
SUB_ACL: DENY:1 SUB_ACL: DENY:1
TGID_TS1_ACL: PERMIT:ALL TGID_TS1_ACL: PERMIT:ALL
TGID_TS2_ACL: PERMIT:ALL TGID_TS2_ACL: PERMIT:ALL
[XLX-1]
MODE: XLXPEER
ENABLED: True
LOOSE: True
EXPORT_AMBE: False
IP:
PORT: 54002
MASTER_IP: 172.16.1.1
MASTER_PORT: 62030
PASSPHRASE: passw0rd
CALLSIGN: W1ABC
RADIO_ID: 312000
RX_FREQ: 449000000
TX_FREQ: 444000000
TX_POWER: 25
COLORCODE: 1
SLOTS: 1
LATITUDE: 38.0000
LONGITUDE: -095.0000
HEIGHT: 75
LOCATION: Anywhere, USA
DESCRIPTION: This is a cool repeater
URL: www.w1abc.org
SOFTWARE_ID: 20170620
PACKAGE_ID: MMDVM_HBlink
GROUP_HANGTIME: 5
XLXMODULE: 4004
USE_ACL: True
SUB_ACL: DENY:1
TGID_TS1_ACL: PERMIT:ALL
TGID_TS2_ACL: PERMIT:ALL

176
hblink.py
View File

@ -1,7 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
############################################################################### ###############################################################################
# Copyright (C) 2016-2019 Cortney T. Buffington, N0MJS <n0mjs@me.com> # Copyright (C) 2016-2020 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
@ -45,11 +45,7 @@ from twisted.internet import reactor, task
import log import log
import config import config
from const import * from const import *
from dmr_utils3.utils import int_id, bytes_4, try_download, mk_id_dict from dmr_utils3.utils import int_id, bytes_4
# Imports for the reporting server
import pickle
from reporting_const import *
# The module needs logging logging, but handlers, etc. are controlled by the parent # The module needs logging logging, but handlers, etc. are controlled by the parent
import logging import logging
@ -66,23 +62,6 @@ __email__ = 'n0mjs@me.com'
# Global variables used whether we are a module or __main__ # Global variables used whether we are a module or __main__
systems = {} systems = {}
# Timed loop used for reporting HBP status
def config_reports(_config, _factory):
def reporting_loop(_logger, _server):
_logger.debug('(GLOBAL) Periodic reporting loop started')
_server.send_config()
logger.info('(GLOBAL) HBlink TCP reporting server configured')
report_server = _factory(_config)
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'])
return report_server
# Shut ourselves down gracefully by disconnecting from the masters and peers. # Shut ourselves down gracefully by disconnecting from the masters and peers.
def hblink_handler(_signal, _frame): def hblink_handler(_signal, _frame):
@ -105,11 +84,10 @@ def acl_check(_id, _acl):
#************************************************ #************************************************
class OPENBRIDGE(DatagramProtocol): class OPENBRIDGE(DatagramProtocol):
def __init__(self, _name, _config, _report): def __init__(self, _name, _config):
# Define a few shortcuts to make the rest of the class more readable # Define a few shortcuts to make the rest of the class more readable
self._CONFIG = _config self._CONFIG = _config
self._system = _name self._system = _name
self._report = _report
self._config = self._CONFIG['SYSTEMS'][self._system] self._config = self._CONFIG['SYSTEMS'][self._system]
self._laststrid = deque([], 20) self._laststrid = deque([], 20)
@ -200,11 +178,10 @@ class OPENBRIDGE(DatagramProtocol):
#************************************************ #************************************************
class HBSYSTEM(DatagramProtocol): class HBSYSTEM(DatagramProtocol):
def __init__(self, _name, _config, _report): def __init__(self, _name, _config):
# Define a few shortcuts to make the rest of the class more readable # Define a few shortcuts to make the rest of the class more readable
self._CONFIG = _config self._CONFIG = _config
self._system = _name self._system = _name
self._report = _report
self._config = self._CONFIG['SYSTEMS'][self._system] self._config = self._CONFIG['SYSTEMS'][self._system]
self._laststrid = {1: b'', 2: b''} self._laststrid = {1: b'', 2: b''}
@ -223,13 +200,6 @@ class HBSYSTEM(DatagramProtocol):
self.datagramReceived = self.peer_datagramReceived self.datagramReceived = self.peer_datagramReceived
self.dereg = self.peer_dereg self.dereg = self.peer_dereg
elif self._config['MODE'] == 'XLXPEER':
self._stats = self._config['XLXSTATS']
self.send_system = self.send_master
self.maintenance_loop = self.peer_maintenance_loop
self.datagramReceived = self.peer_datagramReceived
self.dereg = self.peer_dereg
def startProtocol(self): def startProtocol(self):
# Set up periodic loop for tracking pings from peers. Run every 'PING_TIME' seconds # Set up periodic loop for tracking pings from peers. Run every 'PING_TIME' seconds
self._system_maintenance = task.LoopingCall(self.maintenance_loop) self._system_maintenance = task.LoopingCall(self.maintenance_loop)
@ -279,41 +249,19 @@ class HBSYSTEM(DatagramProtocol):
if _packet[:4] == DMRD: if _packet[:4] == DMRD:
_packet = b''.join([_packet[:11], _peer, _packet[15:]]) _packet = b''.join([_packet[:11], _peer, _packet[15:]])
self.transport.write(_packet, self._peers[_peer]['SOCKADDR']) self.transport.write(_packet, self._peers[_peer]['SOCKADDR'])
# KEEP THE FOLLOWING COMMENTED OUT UNLESS YOU'RE DEBUGGING DEEPLY!!!!
#logger.debug('(%s) TX Packet to %s on port %s: %s', self._peers[_peer]['RADIO_ID'], self._peers[_peer]['IP'], self._peers[_peer]['PORT'], ahex(_packet)) #logger.debug('(%s) TX Packet to %s on port %s: %s', self._peers[_peer]['RADIO_ID'], self._peers[_peer]['IP'], self._peers[_peer]['PORT'], ahex(_packet))
def send_master(self, _packet): def send_master(self, _packet):
if _packet[:4] == DMRD: if _packet[:4] == DMRD:
_packet = b''.join([_packet[:11], self._config['RADIO_ID'], _packet[15:]]) _packet = b''.join([_packet[:11], self._config['RADIO_ID'], _packet[15:]])
self.transport.write(_packet, self._config['MASTER_SOCKADDR']) self.transport.write(_packet, self._config['MASTER_SOCKADDR'])
# KEEP THE FOLLOWING COMMENTED OUT UNLESS YOU'RE DEBUGGING DEEPLY!!!!
# logger.debug('(%s) TX Packet to %s:%s -- %s', self._system, self._config['MASTER_IP'], self._config['MASTER_PORT'], ahex(_packet)) # logger.debug('(%s) TX Packet to %s:%s -- %s', self._system, self._config['MASTER_IP'], self._config['MASTER_PORT'], ahex(_packet))
def send_xlxmaster(self, radio, xlx, mastersock):
radio3 = int.from_bytes(radio, 'big').to_bytes(3, 'big')
radio4 = int.from_bytes(radio, 'big').to_bytes(4, 'big')
xlx3 = xlx.to_bytes(3, 'big')
streamid = randint(0,255).to_bytes(1, 'big')+randint(0,255).to_bytes(1, 'big')+randint(0,255).to_bytes(1, 'big')+randint(0,255).to_bytes(1, 'big')
# Wait for .5 secs for the XLX to log us in
for packetnr in range(5):
if packetnr < 3:
# First 3 packets, voice start, stream type e1
strmtype = 225
payload = bytearray.fromhex('4f2e00b501ae3a001c40a0c1cc7dff57d75df5d5065026f82880bd616f13f185890000')
else:
# Last 2 packets, voice end, stream type e2
strmtype = 226
payload = bytearray.fromhex('4f410061011e3a781c30a061ccbdff57d75df5d2534425c02fe0b1216713e885ba0000')
packetnr1 = packetnr.to_bytes(1, 'big')
strmtype1 = strmtype.to_bytes(1, 'big')
_packet = b''.join([DMRD, packetnr1, radio3, xlx3, radio4, strmtype1, streamid, payload])
self.transport.write(_packet, mastersock)
# KEEP THE FOLLOWING COMMENTED OUT UNLESS YOU'RE DEBUGGING DEEPLY!!!!
#logger.debug('(%s) XLX Module Change Packet: %s', self._system, ahex(_packet))
return
def dmrd_received(self, _peer_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data): def dmrd_received(self, _peer_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data):
pass pass
def dmra_recieved(self, _data):
pass
def master_dereg(self): def master_dereg(self):
for _peer in self._peers: for _peer in self._peers:
@ -326,7 +274,6 @@ class HBSYSTEM(DatagramProtocol):
# Aliased in __init__ to datagramReceived if system is a master # Aliased in __init__ to datagramReceived if system is a master
def master_datagramReceived(self, _data, _sockaddr): def master_datagramReceived(self, _data, _sockaddr):
# Keep This Line Commented Unless HEAVILY Debugging!
# logger.debug('(%s) RX packet from %s -- %s', self._system, _sockaddr, ahex(_data)) # logger.debug('(%s) RX packet from %s -- %s', self._system, _sockaddr, ahex(_data))
# Extract the command, which is various length, all but one 4 significant characters -- RPTCL # Extract the command, which is various length, all but one 4 significant characters -- RPTCL
@ -519,18 +466,12 @@ class HBSYSTEM(DatagramProtocol):
self.transport.write(b''.join([MSTNAK, _peer_id]), _sockaddr) self.transport.write(b''.join([MSTNAK, _peer_id]), _sockaddr)
logger.warning('(%s) Ping from Radio ID that is not logged in: %s', self._system, int_id(_peer_id)) logger.warning('(%s) Ping from Radio ID that is not logged in: %s', self._system, int_id(_peer_id))
elif _command == RPTO: # Talker alias callback
_peer_id = _data[4:8]
if _peer_id in self._peers \
and self._peers[_peer_id]['CONNECTION'] == 'YES' \
and self._peers[_peer_id]['SOCKADDR'] == _sockaddr:
logger.info('(%s) Peer %s (%s) has send options: %s', self._system, self._peers[_peer_id]['CALLSIGN'], int_id(_peer_id), _data[8:])
self.transport.write(b''.join([RPTACK, _peer_id]), _sockaddr)
elif _command == DMRA: elif _command == DMRA:
_peer_id = _data[4:8] self.dmrd_received(_data)
logger.info('(%s) Recieved DMR Talker Alias from peer %s, subscriber %s', self._system, self._peers[_peer_id]['CALLSIGN'], int_id(_rf_src)) #logger.info('(%s) DMRA recieved', self._system)
else: else:
logger.error('(%s) Unrecognized command. Raw HBP PDU: %s', self._system, ahex(_data)) logger.error('(%s) Unrecognized command. Raw HBP PDU: %s', self._system, ahex(_data))
@ -660,12 +601,7 @@ class HBSYSTEM(DatagramProtocol):
self._stats['CONNECTION'] = 'YES' self._stats['CONNECTION'] = 'YES'
self._stats['CONNECTED'] = time() self._stats['CONNECTED'] = time()
logger.info('(%s) Connection to Master Completed', self._system) logger.info('(%s) Connection to Master Completed', self._system)
# If we are an XLX, send the XLX module request here.
if self._config['MODE'] == 'XLXPEER':
self.send_xlxmaster(self._config['RADIO_ID'], int(4000), self._config['MASTER_SOCKADDR'])
self.send_xlxmaster(self._config['RADIO_ID'], self._config['XLXMODULE'], self._config['MASTER_SOCKADDR'])
logger.info('(%s) Sending XLX Module request', self._system)
else: else:
self._stats['CONNECTION'] = 'NO' self._stats['CONNECTION'] = 'NO'
logger.error('(%s) Master ACK Contained wrong ID - Connection Reset', self._system) logger.error('(%s) Master ACK Contained wrong ID - Connection Reset', self._system)
@ -697,79 +633,7 @@ class HBSYSTEM(DatagramProtocol):
else: else:
logger.error('(%s) Received an invalid command in packet: %s', self._system, ahex(_data)) logger.error('(%s) Received an invalid command in packet: %s', self._system, ahex(_data))
#
# Socket-based reporting section
#
class report(NetstringReceiver):
def __init__(self, factory):
self._factory = factory
def connectionMade(self):
self._factory.clients.append(self)
logger.info('(REPORT) HBlink reporting client connected: %s', self.transport.getPeer())
def connectionLost(self, reason):
logger.info('(REPORT) HBlink 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 == REPORT_OPCODES['CONFIG_REQ']:
logger.info('(REPORT) HBlink reporting client sent \'CONFIG_REQ\': %s', self.transport.getPeer())
self.send_config()
else:
logger.error('(REPORT) got unknown opcode')
class reportFactory(Factory):
def __init__(self, config):
self._config = config
def buildProtocol(self, addr):
if (addr.host) in self._config['REPORTS']['REPORT_CLIENTS'] or '*' in self._config['REPORTS']['REPORT_CLIENTS']:
logger.debug('(REPORT) Permitting report server connection attempt from: %s:%s', addr.host, addr.port)
return report(self)
else:
logger.error('(REPORT) Invalid report server connection attempt from: %s:%s', addr.host, addr.port)
return None
def send_clients(self, _message):
for client in self.clients:
client.sendString(_message)
def send_config(self):
serialized = pickle.dumps(self._config['SYSTEMS'], protocol=2) #.decode('utf-8', errors='ignore') #pickle.HIGHEST_PROTOCOL)
self.send_clients(b''.join([REPORT_OPCODES['CONFIG_SND'], serialized]))
# ID ALIAS CREATION
# Download
def mk_aliases(_config):
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('(GLOBAL) %s', 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('(GLOBAL) %s', result)
# Make Dictionaries
peer_ids = mk_id_dict(_config['ALIASES']['PATH'], _config['ALIASES']['PEER_FILE'])
if peer_ids:
logger.info('(GLOBAL) 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('(GLOBAL) 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('(GLOBAL) ID ALIAS MAPPER: talkgroup_ids dictionary is available')
return peer_ids, subscriber_ids, talkgroup_ids
#************************************************ #************************************************
# MAIN PROGRAM LOOP STARTS HERE # MAIN PROGRAM LOOP STARTS HERE
@ -816,23 +680,15 @@ if __name__ == '__main__':
for sig in [signal.SIGTERM, signal.SIGINT]: for sig in [signal.SIGTERM, signal.SIGINT]:
signal.signal(sig, sig_handler) signal.signal(sig, sig_handler)
peer_ids, subscriber_ids, talkgroup_ids = mk_aliases(CONFIG)
# INITIALIZE THE REPORTING LOOP
if CONFIG['REPORTS']['REPORT']:
report_server = config_reports(CONFIG, reportFactory)
else:
report_server = None
logger.info('(REPORT) TCP Socket reporting not configured')
# HBlink instance creation # HBlink instance creation
logger.info('(GLOBAL) HBlink \'HBlink.py\' -- SYSTEM STARTING...') logger.info('(GLOBAL) HBlink \'HBlink.py\' -- SYSTEM STARTING...')
for system in CONFIG['SYSTEMS']: for system in CONFIG['SYSTEMS']:
if CONFIG['SYSTEMS'][system]['ENABLED']: if CONFIG['SYSTEMS'][system]['ENABLED']:
if CONFIG['SYSTEMS'][system]['MODE'] == 'OPENBRIDGE': if CONFIG['SYSTEMS'][system]['MODE'] == 'OPENBRIDGE':
systems[system] = OPENBRIDGE(system, CONFIG, report_server) systems[system] = OPENBRIDGE(system, CONFIG)
else: else:
systems[system] = HBSYSTEM(system, CONFIG, report_server) systems[system] = HBSYSTEM(system, CONFIG)
reactor.listenUDP(CONFIG['SYSTEMS'][system]['PORT'], systems[system], interface=CONFIG['SYSTEMS'][system]['IP']) reactor.listenUDP(CONFIG['SYSTEMS'][system]['PORT'], systems[system], interface=CONFIG['SYSTEMS'][system]['IP'])
logger.debug('(GLOBAL) %s instance created: %s, %s', CONFIG['SYSTEMS'][system]['MODE'], system, systems[system]) logger.debug('(GLOBAL) %s instance created: %s, %s', CONFIG['SYSTEMS'][system]['MODE'], system, systems[system])

6
install.sh Executable file
View File

@ -0,0 +1,6 @@
#! /bin/bash
# Install the required support programs
apt-get install python3 python3-pip -y
pip3 install -r requirements.txt

16
rules-750.py Executable file
View File

@ -0,0 +1,16 @@
BRIDGES = {
'1/2': [
{'SYSTEM': '444.750', 'TS': 1, 'TGID': 2, 'ACTIVE': True, 'TIMEOUT': 5,'TO_TYPE': 'NONE', 'ON': [], 'OFF': [], 'RESET': []},
{'SYSTEM': 'OBP', 'TS': 1, 'TGID': 2, 'ACTIVE': True, 'TIMEOUT': 5,'TO_TYPE': 'NONE', 'ON': [], 'OFF': [], 'RESET': []}
],
'KANSAS': [
{'SYSTEM': '444.750', 'TS': 2, 'TGID': 3120, 'ACTIVE': True, 'TIMEOUT': 5,'TO_TYPE': 'NONE', 'ON': [], 'OFF': [], 'RESET': []},
{'SYSTEM': 'OBP', 'TS': 1, 'TGID': 3120, 'ACTIVE': True, 'TIMEOUT': 5,'TO_TYPE': 'NONE', 'ON': [], 'OFF': [], 'RESET': []}
]
}
UNIT = ['444.750', 'OBP']
if __name__ == '__main__':
from pprint import pprint
pprint(BRIDGES)

16
rules-800.py Executable file
View File

@ -0,0 +1,16 @@
BRIDGES = {
'1/2': [
{'SYSTEM': '444.800', 'TS': 1, 'TGID': 2, 'ACTIVE': True, 'TIMEOUT': 5,'TO_TYPE': 'NONE', 'ON': [], 'OFF': [], 'RESET': []},
{'SYSTEM': 'OBP', 'TS': 1, 'TGID': 2, 'ACTIVE': True, 'TIMEOUT': 5,'TO_TYPE': 'NONE', 'ON': [], 'OFF': [], 'RESET': []}
],
'KANSAS': [
{'SYSTEM': '444.800', 'TS': 2, 'TGID': 3120, 'ACTIVE': True, 'TIMEOUT': 5,'TO_TYPE': 'NONE', 'ON': [], 'OFF': [], 'RESET': []},
{'SYSTEM': 'OBP', 'TS': 1, 'TGID': 3120, 'ACTIVE': True, 'TIMEOUT': 5,'TO_TYPE': 'NONE', 'ON': [], 'OFF': [], 'RESET': []}
]
}
UNIT = ["444.800", "OBP"]
if __name__ == '__main__':
from pprint import pprint
pprint(BRIDGES)