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:
Cort BuffingtonandClaude Opus 4.8 committed 2026-06-25 07:13:59 -05:00
1 parent 59c5072613
commit fdf1b33ee8
2 files changed
+30 -2

No files matched your search

+12 -2
View File
@@ -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
+18
View File
@@ -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()