HIGH PROFILE FIXES - Addresses Brandmeister disconnects.

Random:
  * Commented out per-packet debug logger lines (uncomment locally when needed)

Problems Fixed:

CLIENT MODE: hblink.py

hblink.py did not properly parse the radio_id in a MSTNAK message. We searched for
"MSTN" - 4 characters, and neglected to parse the radio ID at offset 6, after "MSTNAK"
instead of after 4, "MSTN".... rookie mistake:

  elif _command == 'MSTN':    # Actually MSTNAK -- a NACK from the master
  -                _radio_id = _data[4:8]
  +                _radio_id = _data[6:10]

hblink.py did not actually keep track of missed keep-alives correctly to reset a connection.
This has probably been fixed. At worst, no more broken than it was. Would like to have
some folks test it by breaking connectivty froma client to master and watch what happens.

Appears to work now.
This commit is contained in:
Cort Buffington 2018-02-02 14:14:30 -06:00
parent 382d594adf
commit 050aad7cb3
3 changed files with 17 additions and 9 deletions

View File

@ -134,7 +134,7 @@ class bridgeallSYSTEM(HBSYSTEM):
for _target in self._CONFIG['SYSTEMS']:
if _target != self._system:
systems[_target].send_system(_data)
self._logger.debug('(%s) Packet routed to system: %s', self._system, _target)
#self._logger.debug('(%s) Packet routed to system: %s', self._system, _target)
# Final actions - Is this a voice terminator?
@ -228,4 +228,4 @@ if __name__ == '__main__':
reactor.listenUDP(CONFIG['SYSTEMS'][system]['PORT'], systems[system], interface=CONFIG['SYSTEMS'][system]['IP'])
logger.debug('%s instance created: %s, %s', CONFIG['SYSTEMS'][system]['MODE'], system, systems[system])
reactor.run()
reactor.run()

View File

@ -121,6 +121,7 @@ def build_config(_config_file):
'CONNECTION': 'NO', # NO, RTPL_SENT, AUTHENTICATED, CONFIG-SENT, YES
'PINGS_SENT': 0,
'PINGS_ACKD': 0,
'NUM_OUTSTANDING': 0,
'PING_OUTSTANDING': False,
'LAST_PING_TX_TIME': 0,
'LAST_PING_ACK_TIME': 0,

View File

@ -154,18 +154,23 @@ class HBSYSTEM(DatagramProtocol):
# Aliased in __init__ to maintenance_loop if system is a client
def client_maintenance_loop(self):
self._logger.debug('(%s) Client maintenance loop started', self._system)
if self._stats['PING_OUTSTANDING']:
self._stats['NUM_OUTSTANDING'] += 1
# If we're not connected, zero out the stats and send a login request RPTL
if self._stats['CONNECTION'] == 'NO' or self._stats['CONNECTION'] == 'RTPL_SENT':
if self._stats['CONNECTION'] == 'NO' or self._stats['CONNECTION'] == 'RPTL_SENT' or self._stats['NUM_OUTSTANDING'] >= self._CONFIG['GLOBAL']['MAX_MISSED']:
self._stats['PINGS_SENT'] = 0
self._stats['PINGS_ACKD'] = 0
self._stats['CONNECTION'] = 'RTPL_SENT'
self._stats['NUM_OUTSTANDING'] = 0
self._stats['PING_OUTSTANDING'] = False
self._stats['CONNECTION'] = 'RPTL_SENT'
self.send_master('RPTL'+self._config['RADIO_ID'])
self._logger.info('(%s) Sending login request to master %s:%s', self._system, self._config['MASTER_IP'], self._config['MASTER_PORT'])
# If we are connected, sent a ping to the master and increment the counter
if self._stats['CONNECTION'] == 'YES':
self.send_master('RPTPING'+self._config['RADIO_ID'])
self._logger.debug('(%s) RPTPING Sent to Master. Total Sent: %s, Total Missed: %s, Currently Outstanding: %s', self._system, self._stats['PINGS_SENT'], self._stats['PINGS_SENT'] - self._stats['PINGS_ACKD'], self._stats['NUM_OUTSTANDING'])
self._stats['PINGS_SENT'] += 1
self._logger.debug('(%s) RPTPING Sent to Master. Pings Since Connected: %s', self._system, self._stats['PINGS_SENT'])
self._stats['PING_OUTSTANDING'] = True
def send_clients(self, _packet):
for _client in self._clients:
@ -230,7 +235,7 @@ class HBSYSTEM(DatagramProtocol):
for _client in self._clients:
if _client != _radio_id:
self.send_client(_client, _data)
self._logger.debug('(%s) Packet on TS%s from %s (%s) for destination ID %s repeated to client: %s (%s) [Stream ID: %s]', self._system, _slot, self._clients[_radio_id]['CALLSIGN'], int_id(_radio_id), int_id(_dst_id), self._clients[_client]['CALLSIGN'], int_id(_client), int_id(_stream_id))
#self._logger.debug('(%s) Packet on TS%s from %s (%s) for destination ID %s repeated to client: %s (%s) [Stream ID: %s]', self._system, _slot, self._clients[_radio_id]['CALLSIGN'], int_id(_radio_id), int_id(_dst_id), self._clients[_client]['CALLSIGN'], int_id(_client), int_id(_stream_id))
# Userland actions -- typically this is the function you subclass for an application
self.dmrd_received(_radio_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data)
@ -381,14 +386,14 @@ class HBSYSTEM(DatagramProtocol):
self.dmrd_received(_radio_id, _rf_src, _dst_id, _seq, _slot, _call_type, _frame_type, _dtype_vseq, _stream_id, _data)
elif _command == 'MSTN': # Actually MSTNAK -- a NACK from the master
_radio_id = _data[4:8]
_radio_id = _data[6:10]
if _radio_id == self._config['RADIO_ID']: # Validate the source and intended target
self._logger.warning('(%s) MSTNAK Received', self._system)
self._logger.warning('(%s) MSTNAK Received. Resetting connection to the Master.', self._system)
self._stats['CONNECTION'] = 'NO' # Disconnect ourselves and re-register
elif _command == 'RPTA': # Actually RPTACK -- an ACK from the master
# Depending on the state, an RPTACK means different things, in each clause, we check and/or set the state
if self._stats['CONNECTION'] == 'RTPL_SENT': # If we've sent a login request...
if self._stats['CONNECTION'] == 'RPTL_SENT': # If we've sent a login request...
_login_int32 = _data[6:10]
self._logger.info('(%s) Repeater Login ACK Received with 32bit ID: %s', self._system, int_id(_login_int32))
_pass_hash = sha256(_login_int32+self._config['PASSPHRASE']).hexdigest()
@ -447,6 +452,8 @@ class HBSYSTEM(DatagramProtocol):
elif _command == 'MSTP': # Actually MSTPONG -- a reply to RPTPING (send by client)
if _data [7:11] == self._config['RADIO_ID']:
self._stats['PING_OUTSTANDING'] = False
self._stats['NUM_OUTSTANDING'] = 0
self._stats['PINGS_ACKD'] += 1
self._logger.debug('(%s) MSTPONG Received. Pongs Since Connected: %s', self._system, self._stats['PINGS_ACKD'])