bridge.py: log slot collisions once per stream, not per frame
The source-side "collided with existing call" warning fired on every dropped frame, flooding the log when streams contend for a slot (e.g. several peers on one master keying the same talkgroup). Gate it on a per-slot RX_COLLISION_SID so each contending stream is reported once, matching the once-per-stream behavior of the target-side contention log. Routing/emissions are unchanged; this is logging volume only. The dashboard's stale-stream reaper stays as-is for defense-in-depth (non-local dashboard, hblink failure, etc.). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
59c5072613
commit
fdf1b33ee8
2 files changed
+30
-2
No files matched your search
@@ -522,6 +522,9 @@ class routerHBP(HBSYSTEM):
|
||||
# sentinel drops the END of any stream whose last burst was vseq 2.
|
||||
'RX_TERMINATED': True,
|
||||
'TX_TERMINATED': True,
|
||||
# Last stream-id we logged a slot collision for, so a contending
|
||||
# stream is reported once -- not once per colliding frame.
|
||||
'RX_COLLISION_SID': b'\x00',
|
||||
'RX_CT': 'GROUP VOICE',
|
||||
'RX_LC': b'\x00',
|
||||
'TX_H_LC': b'\x00',
|
||||
@@ -555,6 +558,9 @@ class routerHBP(HBSYSTEM):
|
||||
# sentinel drops the END of any stream whose last burst was vseq 2.
|
||||
'RX_TERMINATED': True,
|
||||
'TX_TERMINATED': True,
|
||||
# Last stream-id we logged a slot collision for, so a contending
|
||||
# stream is reported once -- not once per colliding frame.
|
||||
'RX_COLLISION_SID': b'\x00',
|
||||
'RX_CT': 'GROUP VOICE',
|
||||
'RX_LC': b'\x00',
|
||||
'TX_H_LC': b'\x00',
|
||||
@@ -598,7 +604,9 @@ class routerHBP(HBSYSTEM):
|
||||
# Is this a new call stream?
|
||||
if (_stream_id != self.STATUS[_slot]['RX_STREAM_ID']):
|
||||
if (not self.STATUS[_slot]['RX_TERMINATED']) and (pkt_time < (self.STATUS[_slot]['RX_TIME'] + STREAM_TO)) and (_rf_src != self.STATUS[_slot]['RX_RFS']):
|
||||
logger.warning('(%s) Packet received with STREAM ID: %s <FROM> SUB: %s PEER: %s <TO> TGID %s, SLOT %s collided with existing call', self._system, int_id(_stream_id), int_id(_rf_src), int_id(_peer_id), int_id(_dst_id), _slot)
|
||||
if self.STATUS[_slot]['RX_COLLISION_SID'] != _stream_id:
|
||||
self.STATUS[_slot]['RX_COLLISION_SID'] = _stream_id
|
||||
logger.warning('(%s) Packet received with STREAM ID: %s <FROM> SUB: %s PEER: %s <TO> TGID %s, SLOT %s collided with existing call', self._system, int_id(_stream_id), int_id(_rf_src), int_id(_peer_id), int_id(_dst_id), _slot)
|
||||
return
|
||||
|
||||
# A new stream is taking the slot: end any prior un-terminated stream first
|
||||
@@ -793,7 +801,9 @@ class routerHBP(HBSYSTEM):
|
||||
|
||||
# Collision in progress, bail out!
|
||||
if (not self.STATUS[_slot]['RX_TERMINATED']) and (pkt_time < (self.STATUS[_slot]['RX_TIME'] + STREAM_TO)) and (_rf_src != self.STATUS[_slot]['RX_RFS']):
|
||||
logger.warning('(%s) Packet received with STREAM ID: %s <FROM> SUB: %s PEER: %s <TO> UNIT %s, SLOT %s collided with existing call', self._system, int_id(_stream_id), int_id(_rf_src), int_id(_peer_id), int_id(_dst_id), _slot)
|
||||
if self.STATUS[_slot]['RX_COLLISION_SID'] != _stream_id:
|
||||
self.STATUS[_slot]['RX_COLLISION_SID'] = _stream_id
|
||||
logger.warning('(%s) Packet received with STREAM ID: %s <FROM> SUB: %s PEER: %s <TO> UNIT %s, SLOT %s collided with existing call', self._system, int_id(_stream_id), int_id(_rf_src), int_id(_peer_id), int_id(_dst_id), _slot)
|
||||
return
|
||||
|
||||
# A new stream is taking the slot: end any prior un-terminated stream first
|
||||
|
||||
@@ -122,5 +122,23 @@ class TestGroupEnd(unittest.TestCase):
|
||||
self.assertEqual(len(_ends(rpt)), 1) # exactly one, not two
|
||||
|
||||
|
||||
class TestCollisionLogging(unittest.TestCase):
|
||||
|
||||
def test_collision_logged_once_per_stream(self):
|
||||
"""A contending stream is reported once, not once per colliding frame."""
|
||||
w, _ = _world()
|
||||
owner = b'\x00\x00\x00\x10'
|
||||
w.feed_group_header('MASTER-1', stream_id=owner, **_KW)
|
||||
w.feed_group_burst('MASTER-1', 1, stream_id=owner, **_KW)
|
||||
collider = dict(_KW, rf_src=bytes_3(312999)) # different subscriber
|
||||
cid = b'\x00\x00\x00\x11'
|
||||
with self.assertLogs('bridge', level='WARNING') as cm:
|
||||
w.feed_group_header('MASTER-1', stream_id=cid, **collider)
|
||||
w.feed_group_burst('MASTER-1', 1, stream_id=cid, **collider)
|
||||
w.feed_group_burst('MASTER-1', 2, stream_id=cid, **collider)
|
||||
hits = [m for m in cm.output if 'collided with existing call' in m]
|
||||
self.assertEqual(len(hits), 1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user