FA-67221 / Railway interlocking logic / Open access
Vital telegram acceptance: sequence wraparound · case 01
The link drops to safe state every time the sequence number wraps.
ROOT CAUSE
The sequence gap is computed without modulo arithmetic.
VERIFIED REPAIR
Compute the forward gap modulo 256.
Unsuccessful approach: Taking the absolute difference accepts replayed telegrams whose number went backwards.
Case contract
Telegrams carry an 8-bit sequence number, send and receive times and a CRC flag. A telegram is good if its CRC is valid, 0 <= rx-tx <= max_age_ms and (after the first good one) its sequence advances by 1..3 modulo 256. The link starts in safe state; if more than timeout_ms passes between the last good telegram and a new arrival the link falls back to safe state. Two consecutive good telegrams leave safe state; any bad telegram resets that run. Output [good, safe] per telegram.
Why this case matters
Interlocking logic decides whether trains may be given authority; a wrong decision at this point either grants unsafe movements or strands traffic.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
last = None
last_t = None
ok_run = 0
safe = True
out = []
for m in x['messages']:
t = m['rx_ms']
if last_t is not None and t - last_t > x['timeout_ms']:
safe = True
ok_run = 0
good = m['crc'] and 0 <= t - m['tx_ms'] <= x['max_age_ms']
if good and last is not None:
gap = m['seq'] - last
good = 1 <= gap <= 3
if good:
last = m['seq']
last_t = t
ok_run += 1
if ok_run >= 2:
safe = False
else:
ok_run = 0
out.append([good, safe])
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: sequence wraps past 255', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 254, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 255, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 0, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [True, False], [True, False]]), ('sampled regression 35', {'timeout_ms': 2000, 'max_age_ms': 300, 'messages': [{'seq': 254, 'rx_ms': 1200, 'tx_ms': 1050, 'crc': True}, {'seq': 255, 'rx_ms': 1300, 'tx_ms': 1001, 'crc': True}, {'seq': 1, 'rx_ms': 1500, 'tx_ms': 1520, 'crc': True}, {'seq': 2, 'rx_ms': 2700, 'tx_ms': 2401, 'crc': True}, {'seq': 2, 'rx_ms': 2800, 'tx_ms': 2499, 'crc': True}, {'seq': 2, 'rx_ms': 5300, 'tx_ms': 5300, 'crc': True}]}, [[True, True], [True, False], [False, False], [True, False], [False, False], [False, True]]), ('boundary: replayed telegram', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 10, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 11, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 10, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [True, False], [False, False]]), ('boundary: duplicate telegram', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 10, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 10, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 11, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [False, True], [True, True]]), ('boundary: two telegrams lost', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 10, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 13, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 14, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [True, False], [True, False]]), ('control 1', {'timeout_ms': 2000, 'max_age_ms': 300, 'messages': [{'seq': 250, 'rx_ms': 2500, 'tx_ms': 2520, 'crc': True}, {'seq': 251, 'rx_ms': 3000, 'tx_ms': 3020, 'crc': True}, {'seq': 253, 'rx_ms': 3100, 'tx_ms': 2500, 'crc': True}, {'seq': 0, 'rx_ms': 3200, 'tx_ms': 2899, 'crc': True}, {'seq': 0, 'rx_ms': 3700, 'tx_ms': 3700, 'crc': True}]}, [[False, True], [False, True], [False, True], [False, True], [True, True]]), ('control 4', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 11, 'rx_ms': 1200, 'tx_ms': 1050, 'crc': True}, {'seq': 13, 'rx_ms': 1300, 'tx_ms': 1000, 'crc': True}, {'seq': 14, 'rx_ms': 1500, 'tx_ms': 1350, 'crc': True}, {'seq': 15, 'rx_ms': 1600, 'tx_ms': 1450, 'crc': False}, {'seq': 16, 'rx_ms': 2100, 'tx_ms': 2100, 'crc': True}, {'seq': 20, 'rx_ms': 2300, 'tx_ms': 2150, 'crc': True}, {'seq': 21, 'rx_ms': 2800, 'tx_ms': 2501, 'crc': True}, {'seq': 22, 'rx_ms': 5300, 'tx_ms': 4999, 'crc': True}, {'seq': 22, 'rx_ms': 5500, 'tx_ms': 5500, 'crc': True}]}, [[True, True], [True, False], [True, False], [False, False], [True, False], [False, False], [False, False], [False, True], [False, True]]), ('control 7', {'timeout_ms': 2000, 'max_age_ms': 300, 'messages': [{'seq': 0, 'rx_ms': 200, 'tx_ms': 190, 'crc': False}, {'seq': 3, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}, {'seq': 3, 'rx_ms': 1500, 'tx_ms': 1200, 'crc': True}, {'seq': 3, 'rx_ms': 2700, 'tx_ms': 2400, 'crc': True}]}, [[False, True], [True, True], [False, True], [False, True]])], [('regression: sequence wraps past 255', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 254, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 255, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 0, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [True, False], [True, False]]), ('boundary: replayed telegram', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 10, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 11, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 10, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [True, False], [False, False]]), ('sampled regression 80', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 0, 'rx_ms': 1200, 'tx_ms': 1220, 'crc': True}, {'seq': 255, 'rx_ms': 1700, 'tx_ms': 1720, 'crc': True}, {'seq': 255, 'rx_ms': 2200, 'tx_ms': 1900, 'crc': True}, {'seq': 2, 'rx_ms': 2700, 'tx_ms': 2690, 'crc': True}, {'seq': 3, 'rx_ms': 2800, 'tx_ms': 2200, 'crc': True}, {'seq': 6, 'rx_ms': 3000, 'tx_ms': 3020, 'crc': True}, {'seq': 10, 'rx_ms': 3200, 'tx_ms': 3220, 'crc': True}, {'seq': 11, 'rx_ms': 3300, 'tx_ms': 2999, 'crc': True}, {'seq': 11, 'rx_ms': 5800, 'tx_ms': 5790, 'crc': True}, {'seq': 11, 'rx_ms': 5900, 'tx_ms': 5599, 'crc': True}]}, [[False, True], [False, True], [True, True], [True, False], [False, False], [False, False], [False, False], [False, False], [False, True], [False, True]]), ('sampled regression 26', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 252, 'rx_ms': 1200, 'tx_ms': 899, 'crc': True}, {'seq': 253, 'rx_ms': 2400, 'tx_ms': 2250, 'crc': True}, {'seq': 254, 'rx_ms': 2900, 'tx_ms': 2890, 'crc': True}, {'seq': 254, 'rx_ms': 4100, 'tx_ms': 4090, 'crc': True}, {'seq': 253, 'rx_ms': 5300, 'tx_ms': 5290, 'crc': True}, {'seq': 0, 'rx_ms': 5400, 'tx_ms': 5390, 'crc': True}, {'seq': 1, 'rx_ms': 5500, 'tx_ms': 5201, 'crc': True}, {'seq': 3, 'rx_ms': 5600, 'tx_ms': 5000, 'crc': False}, {'seq': 2, 'rx_ms': 5700, 'tx_ms': 5720, 'crc': False}, {'seq': 3, 'rx_ms': 5800, 'tx_ms': 5501, 'crc': True}]}, [[False, True], [True, True], [True, False], [False, True], [False, True], [True, True], [True, False], [False, False], [False, False], [True, False]]), ('boundary: duplicate telegram', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 10, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 10, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 11, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [False, True], [True, True]]), ('control 12', {'timeout_ms': 2000, 'max_age_ms': 300, 'messages': [{'seq': 4, 'rx_ms': 1200, 'tx_ms': 901, 'crc': False}, {'seq': 4, 'rx_ms': 2400, 'tx_ms': 2390, 'crc': True}, {'seq': 4, 'rx_ms': 2500, 'tx_ms': 1900, 'crc': True}, {'seq': 4, 'rx_ms': 3000, 'tx_ms': 2700, 'crc': True}, {'seq': 6, 'rx_ms': 3200, 'tx_ms': 3200, 'crc': True}, {'seq': 4, 'rx_ms': 3300, 'tx_ms': 3300, 'crc': True}, {'seq': 8, 'rx_ms': 3400, 'tx_ms': 3420, 'crc': False}, {'seq': 9, 'rx_ms': 3600, 'tx_ms': 3300, 'crc': True}, {'seq': 9, 'rx_ms': 3800, 'tx_ms': 3501, 'crc': True}]}, [[False, True], [True, True], [False, True], [False, True], [True, True], [False, True], [False, True], [True, True], [False, True]]), ('control 15', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 2, 'rx_ms': 100, 'tx_ms': -200, 'crc': True}, {'seq': 6, 'rx_ms': 2600, 'tx_ms': 2600, 'crc': True}, {'seq': 6, 'rx_ms': 5100, 'tx_ms': 4801, 'crc': True}, {'seq': 6, 'rx_ms': 5200, 'tx_ms': 5050, 'crc': True}, {'seq': 6, 'rx_ms': 7700, 'tx_ms': 7550, 'crc': True}]}, [[True, True], [False, True], [False, True], [False, True], [False, True]]), ('control 18', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 251, 'rx_ms': 500, 'tx_ms': 201, 'crc': True}, {'seq': 252, 'rx_ms': 3000, 'tx_ms': 2701, 'crc': True}, {'seq': 254, 'rx_ms': 3500, 'tx_ms': 3199, 'crc': True}, {'seq': 254, 'rx_ms': 4000, 'tx_ms': 4020, 'crc': True}, {'seq': 0, 'rx_ms': 4500, 'tx_ms': 4201, 'crc': True}, {'seq': 0, 'rx_ms': 5000, 'tx_ms': 4400, 'crc': True}, {'seq': 4, 'rx_ms': 7500, 'tx_ms': 7199, 'crc': True}]}, [[True, True], [True, True], [False, True], [False, True], [False, True], [False, True], [False, True]])], [('regression: sequence wraps past 255', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 254, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 255, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 0, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [True, False], [True, False]]), ('sampled regression 66', {'timeout_ms': 2000, 'max_age_ms': 300, 'messages': [{'seq': 254, 'rx_ms': 200, 'tx_ms': 50, 'crc': True}, {'seq': 255, 'rx_ms': 400, 'tx_ms': 101, 'crc': True}, {'seq': 0, 'rx_ms': 2900, 'tx_ms': 2601, 'crc': True}, {'seq': 1, 'rx_ms': 3100, 'tx_ms': 3100, 'crc': True}, {'seq': 5, 'rx_ms': 3200, 'tx_ms': 3050, 'crc': True}, {'seq': 6, 'rx_ms': 3700, 'tx_ms': 3100, 'crc': True}, {'seq': 5, 'rx_ms': 3900, 'tx_ms': 3599, 'crc': True}, {'seq': 6, 'rx_ms': 5100, 'tx_ms': 4799, 'crc': True}]}, [[True, True], [True, False], [True, True], [True, False], [False, False], [False, False], [False, False], [False, False]]), ('control 48', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 253, 'rx_ms': 1200, 'tx_ms': 600, 'crc': True}, {'seq': 253, 'rx_ms': 2400, 'tx_ms': 2420, 'crc': True}, {'seq': 254, 'rx_ms': 4900, 'tx_ms': 4300, 'crc': True}, {'seq': 2, 'rx_ms': 5400, 'tx_ms': 5420, 'crc': True}, {'seq': 6, 'rx_ms': 5900, 'tx_ms': 5300, 'crc': True}, {'seq': 6, 'rx_ms': 6000, 'tx_ms': 5400, 'crc': True}, {'seq': 6, 'rx_ms': 8500, 'tx_ms': 8500, 'crc': True}, {'seq': 5, 'rx_ms': 8600, 'tx_ms': 8590, 'crc': True}]}, [[False, True], [False, True], [False, True], [False, True], [False, True], [False, True], [True, True], [False, True]]), ('boundary: telegram from the future', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 10, 'rx_ms': 100, 'tx_ms': 150, 'crc': True}, {'seq': 11, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 12, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[False, True], [True, True], [True, False]]), ('boundary: stale telegram', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 10, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 11, 'rx_ms': 800, 'tx_ms': 400, 'crc': True}, {'seq': 12, 'rx_ms': 900, 'tx_ms': 890, 'crc': True}]}, [[True, True], [False, True], [True, True]]), ('control 23', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 250, 'rx_ms': 2500, 'tx_ms': 2199, 'crc': True}, {'seq': 254, 'rx_ms': 3700, 'tx_ms': 3401, 'crc': True}, {'seq': 254, 'rx_ms': 3800, 'tx_ms': 3650, 'crc': True}, {'seq': 254, 'rx_ms': 4000, 'tx_ms': 3699, 'crc': True}, {'seq': 254, 'rx_ms': 6500, 'tx_ms': 6350, 'crc': True}, {'seq': 255, 'rx_ms': 6600, 'tx_ms': 6299, 'crc': True}, {'seq': 255, 'rx_ms': 9100, 'tx_ms': 9120, 'crc': True}, {'seq': 0, 'rx_ms': 11600, 'tx_ms': 11000, 'crc': True}, {'seq': 255, 'rx_ms': 12800, 'tx_ms': 12800, 'crc': True}]}, [[False, True], [True, True], [False, True], [False, True], [False, True], [False, True], [False, True], [False, True], [True, True]]), ('sampled regression 26', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 252, 'rx_ms': 1200, 'tx_ms': 899, 'crc': True}, {'seq': 253, 'rx_ms': 2400, 'tx_ms': 2250, 'crc': True}, {'seq': 254, 'rx_ms': 2900, 'tx_ms': 2890, 'crc': True}, {'seq': 254, 'rx_ms': 4100, 'tx_ms': 4090, 'crc': True}, {'seq': 253, 'rx_ms': 5300, 'tx_ms': 5290, 'crc': True}, {'seq': 0, 'rx_ms': 5400, 'tx_ms': 5390, 'crc': True}, {'seq': 1, 'rx_ms': 5500, 'tx_ms': 5201, 'crc': True}, {'seq': 3, 'rx_ms': 5600, 'tx_ms': 5000, 'crc': False}, {'seq': 2, 'rx_ms': 5700, 'tx_ms': 5720, 'crc': False}, {'seq': 3, 'rx_ms': 5800, 'tx_ms': 5501, 'crc': True}]}, [[False, True], [True, True], [True, False], [False, True], [False, True], [True, True], [True, False], [False, False], [False, False], [True, False]]), ('control 29', {'timeout_ms': 2000, 'max_age_ms': 300, 'messages': [{'seq': 255, 'rx_ms': 500, 'tx_ms': -100, 'crc': True}, {'seq': 3, 'rx_ms': 600, 'tx_ms': 600, 'crc': True}, {'seq': 6, 'rx_ms': 3100, 'tx_ms': 3090, 'crc': True}]}, [[False, True], [True, True], [True, True]])], [('regression: sequence wraps past 255', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 254, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 255, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 0, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [True, False], [True, False]]), ('boundary: replayed telegram', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 10, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 11, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 10, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [True, False], [False, False]]), ('sampled regression 27', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 254, 'rx_ms': 100, 'tx_ms': 120, 'crc': True}, {'seq': 255, 'rx_ms': 600, 'tx_ms': 600, 'crc': True}, {'seq': 2, 'rx_ms': 700, 'tx_ms': 550, 'crc': True}, {'seq': 2, 'rx_ms': 1900, 'tx_ms': 1599, 'crc': True}]}, [[False, True], [True, True], [True, False], [False, True]]), ('sampled regression 72', {'timeout_ms': 2000, 'max_age_ms': 300, 'messages': [{'seq': 254, 'rx_ms': 2500, 'tx_ms': 2500, 'crc': True}, {'seq': 253, 'rx_ms': 3700, 'tx_ms': 3720, 'crc': True}, {'seq': 254, 'rx_ms': 3900, 'tx_ms': 3920, 'crc': True}, {'seq': 254, 'rx_ms': 4000, 'tx_ms': 3990, 'crc': False}, {'seq': 253, 'rx_ms': 4200, 'tx_ms': 3600, 'crc': True}, {'seq': 253, 'rx_ms': 4400, 'tx_ms': 4390, 'crc': True}, {'seq': 1, 'rx_ms': 5600, 'tx_ms': 5300, 'crc': True}, {'seq': 1, 'rx_ms': 5700, 'tx_ms': 5690, 'crc': False}]}, [[True, True], [False, True], [False, True], [False, True], [False, True], [False, True], [True, True], [False, True]]), ('boundary: corrupt telegram between good ones', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 1, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 2, 'rx_ms': 200, 'tx_ms': 190, 'crc': False}, {'seq': 3, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}, {'seq': 4, 'rx_ms': 400, 'tx_ms': 390, 'crc': True}]}, [[True, True], [False, True], [True, True], [True, False]]), ('control 34', {'timeout_ms': 2000, 'max_age_ms': 300, 'messages': [{'seq': 255, 'rx_ms': 1200, 'tx_ms': 901, 'crc': False}, {'seq': 1, 'rx_ms': 1700, 'tx_ms': 1400, 'crc': True}, {'seq': 1, 'rx_ms': 4200, 'tx_ms': 4050, 'crc': True}, {'seq': 2, 'rx_ms': 4300, 'tx_ms': 4320, 'crc': True}]}, [[False, True], [True, True], [False, True], [False, True]]), ('control 37', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 251, 'rx_ms': 1200, 'tx_ms': 899, 'crc': True}, {'seq': 252, 'rx_ms': 1700, 'tx_ms': 1700, 'crc': True}, {'seq': 252, 'rx_ms': 2900, 'tx_ms': 2890, 'crc': True}, {'seq': 252, 'rx_ms': 3000, 'tx_ms': 3000, 'crc': True}, {'seq': 253, 'rx_ms': 3100, 'tx_ms': 2800, 'crc': True}, {'seq': 255, 'rx_ms': 4300, 'tx_ms': 4320, 'crc': True}]}, [[False, True], [True, True], [False, True], [False, True], [True, True], [False, True]]), ('control 40', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 254, 'rx_ms': 1200, 'tx_ms': 600, 'crc': True}, {'seq': 254, 'rx_ms': 2400, 'tx_ms': 2100, 'crc': True}, {'seq': 254, 'rx_ms': 2500, 'tx_ms': 2490, 'crc': False}]}, [[False, True], [True, True], [False, True]])], [('regression: sequence wraps past 255', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 254, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 255, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 0, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [True, False], [True, False]]), ('sampled regression 79', {'timeout_ms': 2000, 'max_age_ms': 300, 'messages': [{'seq': 252, 'rx_ms': 500, 'tx_ms': 201, 'crc': True}, {'seq': 255, 'rx_ms': 1700, 'tx_ms': 1550, 'crc': True}, {'seq': 255, 'rx_ms': 2900, 'tx_ms': 2600, 'crc': True}, {'seq': 2, 'rx_ms': 3100, 'tx_ms': 3100, 'crc': True}, {'seq': 2, 'rx_ms': 3600, 'tx_ms': 3590, 'crc': True}, {'seq': 6, 'rx_ms': 4800, 'tx_ms': 4500, 'crc': True}, {'seq': 7, 'rx_ms': 4900, 'tx_ms': 4890, 'crc': True}, {'seq': 7, 'rx_ms': 5400, 'tx_ms': 5390, 'crc': True}]}, [[True, True], [True, False], [False, False], [True, False], [False, False], [False, False], [False, False], [False, True]]), ('boundary: replayed telegram', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 10, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 11, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 10, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [True, False], [False, False]]), ('boundary: timeout exactly reached', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 1, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 2, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 3, 'rx_ms': 1200, 'tx_ms': 1190, 'crc': True}]}, [[True, True], [True, False], [True, False]]), ('boundary: bad telegrams keep the link alive', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 1, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 2, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 9, 'rx_ms': 800, 'tx_ms': 790, 'crc': True}, {'seq': 3, 'rx_ms': 1100, 'tx_ms': 1090, 'crc': True}]}, [[True, True], [True, False], [False, False], [True, False]]), ('control 45', {'timeout_ms': 2000, 'max_age_ms': 300, 'messages': [{'seq': 13, 'rx_ms': 200, 'tx_ms': -100, 'crc': True}, {'seq': 14, 'rx_ms': 300, 'tx_ms': -1, 'crc': True}, {'seq': 18, 'rx_ms': 500, 'tx_ms': 201, 'crc': False}, {'seq': 19, 'rx_ms': 3000, 'tx_ms': 3020, 'crc': True}, {'seq': 17, 'rx_ms': 5500, 'tx_ms': 4900, 'crc': True}, {'seq': 16, 'rx_ms': 5700, 'tx_ms': 5401, 'crc': True}]}, [[True, True], [False, True], [False, True], [False, True], [False, True], [True, True]]), ('control 48', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 253, 'rx_ms': 1200, 'tx_ms': 600, 'crc': True}, {'seq': 253, 'rx_ms': 2400, 'tx_ms': 2420, 'crc': True}, {'seq': 254, 'rx_ms': 4900, 'tx_ms': 4300, 'crc': True}, {'seq': 2, 'rx_ms': 5400, 'tx_ms': 5420, 'crc': True}, {'seq': 6, 'rx_ms': 5900, 'tx_ms': 5300, 'crc': True}, {'seq': 6, 'rx_ms': 6000, 'tx_ms': 5400, 'crc': True}, {'seq': 6, 'rx_ms': 8500, 'tx_ms': 8500, 'crc': True}, {'seq': 5, 'rx_ms': 8600, 'tx_ms': 8590, 'crc': True}]}, [[False, True], [False, True], [False, True], [False, True], [False, True], [False, True], [True, True], [False, True]]), ('sampled regression 51', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 251, 'rx_ms': 1200, 'tx_ms': 1190, 'crc': True}, {'seq': 252, 'rx_ms': 1400, 'tx_ms': 1099, 'crc': True}, {'seq': 253, 'rx_ms': 1900, 'tx_ms': 1920, 'crc': True}, {'seq': 253, 'rx_ms': 2400, 'tx_ms': 2400, 'crc': True}, {'seq': 254, 'rx_ms': 2600, 'tx_ms': 2301, 'crc': True}, {'seq': 0, 'rx_ms': 3100, 'tx_ms': 2801, 'crc': True}, {'seq': 2, 'rx_ms': 3300, 'tx_ms': 2999, 'crc': True}, {'seq': 3, 'rx_ms': 5800, 'tx_ms': 5499, 'crc': True}, {'seq': 4, 'rx_ms': 5900, 'tx_ms': 5601, 'crc': True}, {'seq': 5, 'rx_ms': 6000, 'tx_ms': 6020, 'crc': False}]}, [[True, True], [False, True], [False, True], [True, True], [True, False], [True, False], [False, False], [False, True], [False, True], [False, True]])]]
for label, args, expected in fixtures[N-1]:
check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: sequence wraps past 255 | [[True, True], [True, False], [False, False]] | [[True, True], [True, False], [True, False]] | Failed |
| sampled regression 35 | [[True, True], [True, False], [False, False], [False, False], [False, False], [False, True]] | [[True, True], [True, False], [False, False], [True, False], [False, False], [False, True]] | Failed |
| boundary: replayed telegram | [[True, True], [True, False], [False, False]] | [[True, True], [True, False], [False, False]] | Passed |
| boundary: duplicate telegram | [[True, True], [False, True], [True, True]] | [[True, True], [False, True], [True, True]] | Passed |
| boundary: two telegrams lost | [[True, True], [True, False], [True, False]] | [[True, True], [True, False], [True, False]] | Passed |
| control 1 | [[False, True], [False, True], [False, True], [False, True], [True, True]] | [[False, True], [False, True], [False, True], [False, True], [True, True]] | Passed |
| control 4 | [[True, True], [True, False], [True, False], [False, False], [True, False], [False, False], [False, False], [False, True], [False, True]] | [[True, True], [True, False], [True, False], [False, False], [True, False], [False, False], [False, False], [False, True], [False, True]] | Passed |
| control 7 | [[False, True], [True, True], [False, True], [False, True]] | [[False, True], [True, True], [False, True], [False, True]] | Passed |
SHA-256 / 34004bd5a35aaa7c77758654c1bd8defc50226cf3a0c3a993165e6ef3673851b
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
last = None
last_t = None
ok_run = 0
safe = True
out = []
for m in x['messages']:
t = m['rx_ms']
if last_t is not None and t - last_t > x['timeout_ms']:
safe = True
ok_run = 0
good = m['crc'] and 0 <= t - m['tx_ms'] <= x['max_age_ms']
if good and last is not None:
gap = abs(m['seq'] - last) % 256
good = 1 <= gap <= 3
if good:
last = m['seq']
last_t = t
ok_run += 1
if ok_run >= 2:
safe = False
else:
ok_run = 0
out.append([good, safe])
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: sequence wraps past 255', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 254, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 255, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 0, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [True, False], [True, False]]), ('sampled regression 35', {'timeout_ms': 2000, 'max_age_ms': 300, 'messages': [{'seq': 254, 'rx_ms': 1200, 'tx_ms': 1050, 'crc': True}, {'seq': 255, 'rx_ms': 1300, 'tx_ms': 1001, 'crc': True}, {'seq': 1, 'rx_ms': 1500, 'tx_ms': 1520, 'crc': True}, {'seq': 2, 'rx_ms': 2700, 'tx_ms': 2401, 'crc': True}, {'seq': 2, 'rx_ms': 2800, 'tx_ms': 2499, 'crc': True}, {'seq': 2, 'rx_ms': 5300, 'tx_ms': 5300, 'crc': True}]}, [[True, True], [True, False], [False, False], [True, False], [False, False], [False, True]]), ('boundary: replayed telegram', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 10, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 11, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 10, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [True, False], [False, False]]), ('boundary: duplicate telegram', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 10, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 10, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 11, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [False, True], [True, True]]), ('boundary: two telegrams lost', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 10, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 13, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 14, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [True, False], [True, False]]), ('control 1', {'timeout_ms': 2000, 'max_age_ms': 300, 'messages': [{'seq': 250, 'rx_ms': 2500, 'tx_ms': 2520, 'crc': True}, {'seq': 251, 'rx_ms': 3000, 'tx_ms': 3020, 'crc': True}, {'seq': 253, 'rx_ms': 3100, 'tx_ms': 2500, 'crc': True}, {'seq': 0, 'rx_ms': 3200, 'tx_ms': 2899, 'crc': True}, {'seq': 0, 'rx_ms': 3700, 'tx_ms': 3700, 'crc': True}]}, [[False, True], [False, True], [False, True], [False, True], [True, True]]), ('control 4', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 11, 'rx_ms': 1200, 'tx_ms': 1050, 'crc': True}, {'seq': 13, 'rx_ms': 1300, 'tx_ms': 1000, 'crc': True}, {'seq': 14, 'rx_ms': 1500, 'tx_ms': 1350, 'crc': True}, {'seq': 15, 'rx_ms': 1600, 'tx_ms': 1450, 'crc': False}, {'seq': 16, 'rx_ms': 2100, 'tx_ms': 2100, 'crc': True}, {'seq': 20, 'rx_ms': 2300, 'tx_ms': 2150, 'crc': True}, {'seq': 21, 'rx_ms': 2800, 'tx_ms': 2501, 'crc': True}, {'seq': 22, 'rx_ms': 5300, 'tx_ms': 4999, 'crc': True}, {'seq': 22, 'rx_ms': 5500, 'tx_ms': 5500, 'crc': True}]}, [[True, True], [True, False], [True, False], [False, False], [True, False], [False, False], [False, False], [False, True], [False, True]]), ('control 7', {'timeout_ms': 2000, 'max_age_ms': 300, 'messages': [{'seq': 0, 'rx_ms': 200, 'tx_ms': 190, 'crc': False}, {'seq': 3, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}, {'seq': 3, 'rx_ms': 1500, 'tx_ms': 1200, 'crc': True}, {'seq': 3, 'rx_ms': 2700, 'tx_ms': 2400, 'crc': True}]}, [[False, True], [True, True], [False, True], [False, True]])], [('regression: sequence wraps past 255', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 254, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 255, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 0, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [True, False], [True, False]]), ('boundary: replayed telegram', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 10, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 11, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 10, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [True, False], [False, False]]), ('sampled regression 80', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 0, 'rx_ms': 1200, 'tx_ms': 1220, 'crc': True}, {'seq': 255, 'rx_ms': 1700, 'tx_ms': 1720, 'crc': True}, {'seq': 255, 'rx_ms': 2200, 'tx_ms': 1900, 'crc': True}, {'seq': 2, 'rx_ms': 2700, 'tx_ms': 2690, 'crc': True}, {'seq': 3, 'rx_ms': 2800, 'tx_ms': 2200, 'crc': True}, {'seq': 6, 'rx_ms': 3000, 'tx_ms': 3020, 'crc': True}, {'seq': 10, 'rx_ms': 3200, 'tx_ms': 3220, 'crc': True}, {'seq': 11, 'rx_ms': 3300, 'tx_ms': 2999, 'crc': True}, {'seq': 11, 'rx_ms': 5800, 'tx_ms': 5790, 'crc': True}, {'seq': 11, 'rx_ms': 5900, 'tx_ms': 5599, 'crc': True}]}, [[False, True], [False, True], [True, True], [True, False], [False, False], [False, False], [False, False], [False, False], [False, True], [False, True]]), ('sampled regression 26', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 252, 'rx_ms': 1200, 'tx_ms': 899, 'crc': True}, {'seq': 253, 'rx_ms': 2400, 'tx_ms': 2250, 'crc': True}, {'seq': 254, 'rx_ms': 2900, 'tx_ms': 2890, 'crc': True}, {'seq': 254, 'rx_ms': 4100, 'tx_ms': 4090, 'crc': True}, {'seq': 253, 'rx_ms': 5300, 'tx_ms': 5290, 'crc': True}, {'seq': 0, 'rx_ms': 5400, 'tx_ms': 5390, 'crc': True}, {'seq': 1, 'rx_ms': 5500, 'tx_ms': 5201, 'crc': True}, {'seq': 3, 'rx_ms': 5600, 'tx_ms': 5000, 'crc': False}, {'seq': 2, 'rx_ms': 5700, 'tx_ms': 5720, 'crc': False}, {'seq': 3, 'rx_ms': 5800, 'tx_ms': 5501, 'crc': True}]}, [[False, True], [True, True], [True, False], [False, True], [False, True], [True, True], [True, False], [False, False], [False, False], [True, False]]), ('boundary: duplicate telegram', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 10, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 10, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 11, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [False, True], [True, True]]), ('control 12', {'timeout_ms': 2000, 'max_age_ms': 300, 'messages': [{'seq': 4, 'rx_ms': 1200, 'tx_ms': 901, 'crc': False}, {'seq': 4, 'rx_ms': 2400, 'tx_ms': 2390, 'crc': True}, {'seq': 4, 'rx_ms': 2500, 'tx_ms': 1900, 'crc': True}, {'seq': 4, 'rx_ms': 3000, 'tx_ms': 2700, 'crc': True}, {'seq': 6, 'rx_ms': 3200, 'tx_ms': 3200, 'crc': True}, {'seq': 4, 'rx_ms': 3300, 'tx_ms': 3300, 'crc': True}, {'seq': 8, 'rx_ms': 3400, 'tx_ms': 3420, 'crc': False}, {'seq': 9, 'rx_ms': 3600, 'tx_ms': 3300, 'crc': True}, {'seq': 9, 'rx_ms': 3800, 'tx_ms': 3501, 'crc': True}]}, [[False, True], [True, True], [False, True], [False, True], [True, True], [False, True], [False, True], [True, True], [False, True]]), ('control 15', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 2, 'rx_ms': 100, 'tx_ms': -200, 'crc': True}, {'seq': 6, 'rx_ms': 2600, 'tx_ms': 2600, 'crc': True}, {'seq': 6, 'rx_ms': 5100, 'tx_ms': 4801, 'crc': True}, {'seq': 6, 'rx_ms': 5200, 'tx_ms': 5050, 'crc': True}, {'seq': 6, 'rx_ms': 7700, 'tx_ms': 7550, 'crc': True}]}, [[True, True], [False, True], [False, True], [False, True], [False, True]]), ('control 18', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 251, 'rx_ms': 500, 'tx_ms': 201, 'crc': True}, {'seq': 252, 'rx_ms': 3000, 'tx_ms': 2701, 'crc': True}, {'seq': 254, 'rx_ms': 3500, 'tx_ms': 3199, 'crc': True}, {'seq': 254, 'rx_ms': 4000, 'tx_ms': 4020, 'crc': True}, {'seq': 0, 'rx_ms': 4500, 'tx_ms': 4201, 'crc': True}, {'seq': 0, 'rx_ms': 5000, 'tx_ms': 4400, 'crc': True}, {'seq': 4, 'rx_ms': 7500, 'tx_ms': 7199, 'crc': True}]}, [[True, True], [True, True], [False, True], [False, True], [False, True], [False, True], [False, True]])], [('regression: sequence wraps past 255', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 254, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 255, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 0, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [True, False], [True, False]]), ('sampled regression 66', {'timeout_ms': 2000, 'max_age_ms': 300, 'messages': [{'seq': 254, 'rx_ms': 200, 'tx_ms': 50, 'crc': True}, {'seq': 255, 'rx_ms': 400, 'tx_ms': 101, 'crc': True}, {'seq': 0, 'rx_ms': 2900, 'tx_ms': 2601, 'crc': True}, {'seq': 1, 'rx_ms': 3100, 'tx_ms': 3100, 'crc': True}, {'seq': 5, 'rx_ms': 3200, 'tx_ms': 3050, 'crc': True}, {'seq': 6, 'rx_ms': 3700, 'tx_ms': 3100, 'crc': True}, {'seq': 5, 'rx_ms': 3900, 'tx_ms': 3599, 'crc': True}, {'seq': 6, 'rx_ms': 5100, 'tx_ms': 4799, 'crc': True}]}, [[True, True], [True, False], [True, True], [True, False], [False, False], [False, False], [False, False], [False, False]]), ('control 48', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 253, 'rx_ms': 1200, 'tx_ms': 600, 'crc': True}, {'seq': 253, 'rx_ms': 2400, 'tx_ms': 2420, 'crc': True}, {'seq': 254, 'rx_ms': 4900, 'tx_ms': 4300, 'crc': True}, {'seq': 2, 'rx_ms': 5400, 'tx_ms': 5420, 'crc': True}, {'seq': 6, 'rx_ms': 5900, 'tx_ms': 5300, 'crc': True}, {'seq': 6, 'rx_ms': 6000, 'tx_ms': 5400, 'crc': True}, {'seq': 6, 'rx_ms': 8500, 'tx_ms': 8500, 'crc': True}, {'seq': 5, 'rx_ms': 8600, 'tx_ms': 8590, 'crc': True}]}, [[False, True], [False, True], [False, True], [False, True], [False, True], [False, True], [True, True], [False, True]]), ('boundary: telegram from the future', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 10, 'rx_ms': 100, 'tx_ms': 150, 'crc': True}, {'seq': 11, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 12, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[False, True], [True, True], [True, False]]), ('boundary: stale telegram', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 10, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 11, 'rx_ms': 800, 'tx_ms': 400, 'crc': True}, {'seq': 12, 'rx_ms': 900, 'tx_ms': 890, 'crc': True}]}, [[True, True], [False, True], [True, True]]), ('control 23', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 250, 'rx_ms': 2500, 'tx_ms': 2199, 'crc': True}, {'seq': 254, 'rx_ms': 3700, 'tx_ms': 3401, 'crc': True}, {'seq': 254, 'rx_ms': 3800, 'tx_ms': 3650, 'crc': True}, {'seq': 254, 'rx_ms': 4000, 'tx_ms': 3699, 'crc': True}, {'seq': 254, 'rx_ms': 6500, 'tx_ms': 6350, 'crc': True}, {'seq': 255, 'rx_ms': 6600, 'tx_ms': 6299, 'crc': True}, {'seq': 255, 'rx_ms': 9100, 'tx_ms': 9120, 'crc': True}, {'seq': 0, 'rx_ms': 11600, 'tx_ms': 11000, 'crc': True}, {'seq': 255, 'rx_ms': 12800, 'tx_ms': 12800, 'crc': True}]}, [[False, True], [True, True], [False, True], [False, True], [False, True], [False, True], [False, True], [False, True], [True, True]]), ('sampled regression 26', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 252, 'rx_ms': 1200, 'tx_ms': 899, 'crc': True}, {'seq': 253, 'rx_ms': 2400, 'tx_ms': 2250, 'crc': True}, {'seq': 254, 'rx_ms': 2900, 'tx_ms': 2890, 'crc': True}, {'seq': 254, 'rx_ms': 4100, 'tx_ms': 4090, 'crc': True}, {'seq': 253, 'rx_ms': 5300, 'tx_ms': 5290, 'crc': True}, {'seq': 0, 'rx_ms': 5400, 'tx_ms': 5390, 'crc': True}, {'seq': 1, 'rx_ms': 5500, 'tx_ms': 5201, 'crc': True}, {'seq': 3, 'rx_ms': 5600, 'tx_ms': 5000, 'crc': False}, {'seq': 2, 'rx_ms': 5700, 'tx_ms': 5720, 'crc': False}, {'seq': 3, 'rx_ms': 5800, 'tx_ms': 5501, 'crc': True}]}, [[False, True], [True, True], [True, False], [False, True], [False, True], [True, True], [True, False], [False, False], [False, False], [True, False]]), ('control 29', {'timeout_ms': 2000, 'max_age_ms': 300, 'messages': [{'seq': 255, 'rx_ms': 500, 'tx_ms': -100, 'crc': True}, {'seq': 3, 'rx_ms': 600, 'tx_ms': 600, 'crc': True}, {'seq': 6, 'rx_ms': 3100, 'tx_ms': 3090, 'crc': True}]}, [[False, True], [True, True], [True, True]])], [('regression: sequence wraps past 255', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 254, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 255, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 0, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [True, False], [True, False]]), ('boundary: replayed telegram', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 10, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 11, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 10, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [True, False], [False, False]]), ('sampled regression 27', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 254, 'rx_ms': 100, 'tx_ms': 120, 'crc': True}, {'seq': 255, 'rx_ms': 600, 'tx_ms': 600, 'crc': True}, {'seq': 2, 'rx_ms': 700, 'tx_ms': 550, 'crc': True}, {'seq': 2, 'rx_ms': 1900, 'tx_ms': 1599, 'crc': True}]}, [[False, True], [True, True], [True, False], [False, True]]), ('sampled regression 72', {'timeout_ms': 2000, 'max_age_ms': 300, 'messages': [{'seq': 254, 'rx_ms': 2500, 'tx_ms': 2500, 'crc': True}, {'seq': 253, 'rx_ms': 3700, 'tx_ms': 3720, 'crc': True}, {'seq': 254, 'rx_ms': 3900, 'tx_ms': 3920, 'crc': True}, {'seq': 254, 'rx_ms': 4000, 'tx_ms': 3990, 'crc': False}, {'seq': 253, 'rx_ms': 4200, 'tx_ms': 3600, 'crc': True}, {'seq': 253, 'rx_ms': 4400, 'tx_ms': 4390, 'crc': True}, {'seq': 1, 'rx_ms': 5600, 'tx_ms': 5300, 'crc': True}, {'seq': 1, 'rx_ms': 5700, 'tx_ms': 5690, 'crc': False}]}, [[True, True], [False, True], [False, True], [False, True], [False, True], [False, True], [True, True], [False, True]]), ('boundary: corrupt telegram between good ones', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 1, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 2, 'rx_ms': 200, 'tx_ms': 190, 'crc': False}, {'seq': 3, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}, {'seq': 4, 'rx_ms': 400, 'tx_ms': 390, 'crc': True}]}, [[True, True], [False, True], [True, True], [True, False]]), ('control 34', {'timeout_ms': 2000, 'max_age_ms': 300, 'messages': [{'seq': 255, 'rx_ms': 1200, 'tx_ms': 901, 'crc': False}, {'seq': 1, 'rx_ms': 1700, 'tx_ms': 1400, 'crc': True}, {'seq': 1, 'rx_ms': 4200, 'tx_ms': 4050, 'crc': True}, {'seq': 2, 'rx_ms': 4300, 'tx_ms': 4320, 'crc': True}]}, [[False, True], [True, True], [False, True], [False, True]]), ('control 37', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 251, 'rx_ms': 1200, 'tx_ms': 899, 'crc': True}, {'seq': 252, 'rx_ms': 1700, 'tx_ms': 1700, 'crc': True}, {'seq': 252, 'rx_ms': 2900, 'tx_ms': 2890, 'crc': True}, {'seq': 252, 'rx_ms': 3000, 'tx_ms': 3000, 'crc': True}, {'seq': 253, 'rx_ms': 3100, 'tx_ms': 2800, 'crc': True}, {'seq': 255, 'rx_ms': 4300, 'tx_ms': 4320, 'crc': True}]}, [[False, True], [True, True], [False, True], [False, True], [True, True], [False, True]]), ('control 40', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 254, 'rx_ms': 1200, 'tx_ms': 600, 'crc': True}, {'seq': 254, 'rx_ms': 2400, 'tx_ms': 2100, 'crc': True}, {'seq': 254, 'rx_ms': 2500, 'tx_ms': 2490, 'crc': False}]}, [[False, True], [True, True], [False, True]])], [('regression: sequence wraps past 255', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 254, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 255, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 0, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [True, False], [True, False]]), ('sampled regression 79', {'timeout_ms': 2000, 'max_age_ms': 300, 'messages': [{'seq': 252, 'rx_ms': 500, 'tx_ms': 201, 'crc': True}, {'seq': 255, 'rx_ms': 1700, 'tx_ms': 1550, 'crc': True}, {'seq': 255, 'rx_ms': 2900, 'tx_ms': 2600, 'crc': True}, {'seq': 2, 'rx_ms': 3100, 'tx_ms': 3100, 'crc': True}, {'seq': 2, 'rx_ms': 3600, 'tx_ms': 3590, 'crc': True}, {'seq': 6, 'rx_ms': 4800, 'tx_ms': 4500, 'crc': True}, {'seq': 7, 'rx_ms': 4900, 'tx_ms': 4890, 'crc': True}, {'seq': 7, 'rx_ms': 5400, 'tx_ms': 5390, 'crc': True}]}, [[True, True], [True, False], [False, False], [True, False], [False, False], [False, False], [False, False], [False, True]]), ('boundary: replayed telegram', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 10, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 11, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 10, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [True, False], [False, False]]), ('boundary: timeout exactly reached', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 1, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 2, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 3, 'rx_ms': 1200, 'tx_ms': 1190, 'crc': True}]}, [[True, True], [True, False], [True, False]]), ('boundary: bad telegrams keep the link alive', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 1, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 2, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 9, 'rx_ms': 800, 'tx_ms': 790, 'crc': True}, {'seq': 3, 'rx_ms': 1100, 'tx_ms': 1090, 'crc': True}]}, [[True, True], [True, False], [False, False], [True, False]]), ('control 45', {'timeout_ms': 2000, 'max_age_ms': 300, 'messages': [{'seq': 13, 'rx_ms': 200, 'tx_ms': -100, 'crc': True}, {'seq': 14, 'rx_ms': 300, 'tx_ms': -1, 'crc': True}, {'seq': 18, 'rx_ms': 500, 'tx_ms': 201, 'crc': False}, {'seq': 19, 'rx_ms': 3000, 'tx_ms': 3020, 'crc': True}, {'seq': 17, 'rx_ms': 5500, 'tx_ms': 4900, 'crc': True}, {'seq': 16, 'rx_ms': 5700, 'tx_ms': 5401, 'crc': True}]}, [[True, True], [False, True], [False, True], [False, True], [False, True], [True, True]]), ('control 48', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 253, 'rx_ms': 1200, 'tx_ms': 600, 'crc': True}, {'seq': 253, 'rx_ms': 2400, 'tx_ms': 2420, 'crc': True}, {'seq': 254, 'rx_ms': 4900, 'tx_ms': 4300, 'crc': True}, {'seq': 2, 'rx_ms': 5400, 'tx_ms': 5420, 'crc': True}, {'seq': 6, 'rx_ms': 5900, 'tx_ms': 5300, 'crc': True}, {'seq': 6, 'rx_ms': 6000, 'tx_ms': 5400, 'crc': True}, {'seq': 6, 'rx_ms': 8500, 'tx_ms': 8500, 'crc': True}, {'seq': 5, 'rx_ms': 8600, 'tx_ms': 8590, 'crc': True}]}, [[False, True], [False, True], [False, True], [False, True], [False, True], [False, True], [True, True], [False, True]]), ('sampled regression 51', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 251, 'rx_ms': 1200, 'tx_ms': 1190, 'crc': True}, {'seq': 252, 'rx_ms': 1400, 'tx_ms': 1099, 'crc': True}, {'seq': 253, 'rx_ms': 1900, 'tx_ms': 1920, 'crc': True}, {'seq': 253, 'rx_ms': 2400, 'tx_ms': 2400, 'crc': True}, {'seq': 254, 'rx_ms': 2600, 'tx_ms': 2301, 'crc': True}, {'seq': 0, 'rx_ms': 3100, 'tx_ms': 2801, 'crc': True}, {'seq': 2, 'rx_ms': 3300, 'tx_ms': 2999, 'crc': True}, {'seq': 3, 'rx_ms': 5800, 'tx_ms': 5499, 'crc': True}, {'seq': 4, 'rx_ms': 5900, 'tx_ms': 5601, 'crc': True}, {'seq': 5, 'rx_ms': 6000, 'tx_ms': 6020, 'crc': False}]}, [[True, True], [False, True], [False, True], [True, True], [True, False], [True, False], [False, False], [False, True], [False, True], [False, True]])]]
for label, args, expected in fixtures[N-1]:
check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: sequence wraps past 255 | [[True, True], [True, False], [False, False]] | [[True, True], [True, False], [True, False]] | Failed |
| sampled regression 35 | [[True, True], [True, False], [False, False], [False, False], [False, False], [False, True]] | [[True, True], [True, False], [False, False], [True, False], [False, False], [False, True]] | Failed |
| boundary: replayed telegram | [[True, True], [True, False], [True, False]] | [[True, True], [True, False], [False, False]] | Failed |
| boundary: duplicate telegram | [[True, True], [False, True], [True, True]] | [[True, True], [False, True], [True, True]] | Passed |
| boundary: two telegrams lost | [[True, True], [True, False], [True, False]] | [[True, True], [True, False], [True, False]] | Passed |
| control 1 | [[False, True], [False, True], [False, True], [False, True], [True, True]] | [[False, True], [False, True], [False, True], [False, True], [True, True]] | Passed |
| control 4 | [[True, True], [True, False], [True, False], [False, False], [True, False], [False, False], [False, False], [False, True], [False, True]] | [[True, True], [True, False], [True, False], [False, False], [True, False], [False, False], [False, False], [False, True], [False, True]] | Passed |
| control 7 | [[False, True], [True, True], [False, True], [False, True]] | [[False, True], [True, True], [False, True], [False, True]] | Passed |
SHA-256 / a8f7ccf3ef8be1da5c98f69d8b98a868fa3ef87f596a4eb28cdebff542bed6fe
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
last = None
last_t = None
ok_run = 0
safe = True
out = []
for m in x['messages']:
t = m['rx_ms']
if last_t is not None and t - last_t > x['timeout_ms']:
safe = True
ok_run = 0
good = m['crc'] and 0 <= t - m['tx_ms'] <= x['max_age_ms']
if good and last is not None:
gap = (m['seq'] - last) % 256
good = 1 <= gap <= 3
if good:
last = m['seq']
last_t = t
ok_run += 1
if ok_run >= 2:
safe = False
else:
ok_run = 0
out.append([good, safe])
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: sequence wraps past 255', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 254, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 255, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 0, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [True, False], [True, False]]), ('sampled regression 35', {'timeout_ms': 2000, 'max_age_ms': 300, 'messages': [{'seq': 254, 'rx_ms': 1200, 'tx_ms': 1050, 'crc': True}, {'seq': 255, 'rx_ms': 1300, 'tx_ms': 1001, 'crc': True}, {'seq': 1, 'rx_ms': 1500, 'tx_ms': 1520, 'crc': True}, {'seq': 2, 'rx_ms': 2700, 'tx_ms': 2401, 'crc': True}, {'seq': 2, 'rx_ms': 2800, 'tx_ms': 2499, 'crc': True}, {'seq': 2, 'rx_ms': 5300, 'tx_ms': 5300, 'crc': True}]}, [[True, True], [True, False], [False, False], [True, False], [False, False], [False, True]]), ('boundary: replayed telegram', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 10, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 11, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 10, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [True, False], [False, False]]), ('boundary: duplicate telegram', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 10, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 10, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 11, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [False, True], [True, True]]), ('boundary: two telegrams lost', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 10, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 13, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 14, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [True, False], [True, False]]), ('control 1', {'timeout_ms': 2000, 'max_age_ms': 300, 'messages': [{'seq': 250, 'rx_ms': 2500, 'tx_ms': 2520, 'crc': True}, {'seq': 251, 'rx_ms': 3000, 'tx_ms': 3020, 'crc': True}, {'seq': 253, 'rx_ms': 3100, 'tx_ms': 2500, 'crc': True}, {'seq': 0, 'rx_ms': 3200, 'tx_ms': 2899, 'crc': True}, {'seq': 0, 'rx_ms': 3700, 'tx_ms': 3700, 'crc': True}]}, [[False, True], [False, True], [False, True], [False, True], [True, True]]), ('control 4', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 11, 'rx_ms': 1200, 'tx_ms': 1050, 'crc': True}, {'seq': 13, 'rx_ms': 1300, 'tx_ms': 1000, 'crc': True}, {'seq': 14, 'rx_ms': 1500, 'tx_ms': 1350, 'crc': True}, {'seq': 15, 'rx_ms': 1600, 'tx_ms': 1450, 'crc': False}, {'seq': 16, 'rx_ms': 2100, 'tx_ms': 2100, 'crc': True}, {'seq': 20, 'rx_ms': 2300, 'tx_ms': 2150, 'crc': True}, {'seq': 21, 'rx_ms': 2800, 'tx_ms': 2501, 'crc': True}, {'seq': 22, 'rx_ms': 5300, 'tx_ms': 4999, 'crc': True}, {'seq': 22, 'rx_ms': 5500, 'tx_ms': 5500, 'crc': True}]}, [[True, True], [True, False], [True, False], [False, False], [True, False], [False, False], [False, False], [False, True], [False, True]]), ('control 7', {'timeout_ms': 2000, 'max_age_ms': 300, 'messages': [{'seq': 0, 'rx_ms': 200, 'tx_ms': 190, 'crc': False}, {'seq': 3, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}, {'seq': 3, 'rx_ms': 1500, 'tx_ms': 1200, 'crc': True}, {'seq': 3, 'rx_ms': 2700, 'tx_ms': 2400, 'crc': True}]}, [[False, True], [True, True], [False, True], [False, True]])], [('regression: sequence wraps past 255', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 254, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 255, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 0, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [True, False], [True, False]]), ('boundary: replayed telegram', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 10, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 11, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 10, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [True, False], [False, False]]), ('sampled regression 80', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 0, 'rx_ms': 1200, 'tx_ms': 1220, 'crc': True}, {'seq': 255, 'rx_ms': 1700, 'tx_ms': 1720, 'crc': True}, {'seq': 255, 'rx_ms': 2200, 'tx_ms': 1900, 'crc': True}, {'seq': 2, 'rx_ms': 2700, 'tx_ms': 2690, 'crc': True}, {'seq': 3, 'rx_ms': 2800, 'tx_ms': 2200, 'crc': True}, {'seq': 6, 'rx_ms': 3000, 'tx_ms': 3020, 'crc': True}, {'seq': 10, 'rx_ms': 3200, 'tx_ms': 3220, 'crc': True}, {'seq': 11, 'rx_ms': 3300, 'tx_ms': 2999, 'crc': True}, {'seq': 11, 'rx_ms': 5800, 'tx_ms': 5790, 'crc': True}, {'seq': 11, 'rx_ms': 5900, 'tx_ms': 5599, 'crc': True}]}, [[False, True], [False, True], [True, True], [True, False], [False, False], [False, False], [False, False], [False, False], [False, True], [False, True]]), ('sampled regression 26', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 252, 'rx_ms': 1200, 'tx_ms': 899, 'crc': True}, {'seq': 253, 'rx_ms': 2400, 'tx_ms': 2250, 'crc': True}, {'seq': 254, 'rx_ms': 2900, 'tx_ms': 2890, 'crc': True}, {'seq': 254, 'rx_ms': 4100, 'tx_ms': 4090, 'crc': True}, {'seq': 253, 'rx_ms': 5300, 'tx_ms': 5290, 'crc': True}, {'seq': 0, 'rx_ms': 5400, 'tx_ms': 5390, 'crc': True}, {'seq': 1, 'rx_ms': 5500, 'tx_ms': 5201, 'crc': True}, {'seq': 3, 'rx_ms': 5600, 'tx_ms': 5000, 'crc': False}, {'seq': 2, 'rx_ms': 5700, 'tx_ms': 5720, 'crc': False}, {'seq': 3, 'rx_ms': 5800, 'tx_ms': 5501, 'crc': True}]}, [[False, True], [True, True], [True, False], [False, True], [False, True], [True, True], [True, False], [False, False], [False, False], [True, False]]), ('boundary: duplicate telegram', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 10, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 10, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 11, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [False, True], [True, True]]), ('control 12', {'timeout_ms': 2000, 'max_age_ms': 300, 'messages': [{'seq': 4, 'rx_ms': 1200, 'tx_ms': 901, 'crc': False}, {'seq': 4, 'rx_ms': 2400, 'tx_ms': 2390, 'crc': True}, {'seq': 4, 'rx_ms': 2500, 'tx_ms': 1900, 'crc': True}, {'seq': 4, 'rx_ms': 3000, 'tx_ms': 2700, 'crc': True}, {'seq': 6, 'rx_ms': 3200, 'tx_ms': 3200, 'crc': True}, {'seq': 4, 'rx_ms': 3300, 'tx_ms': 3300, 'crc': True}, {'seq': 8, 'rx_ms': 3400, 'tx_ms': 3420, 'crc': False}, {'seq': 9, 'rx_ms': 3600, 'tx_ms': 3300, 'crc': True}, {'seq': 9, 'rx_ms': 3800, 'tx_ms': 3501, 'crc': True}]}, [[False, True], [True, True], [False, True], [False, True], [True, True], [False, True], [False, True], [True, True], [False, True]]), ('control 15', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 2, 'rx_ms': 100, 'tx_ms': -200, 'crc': True}, {'seq': 6, 'rx_ms': 2600, 'tx_ms': 2600, 'crc': True}, {'seq': 6, 'rx_ms': 5100, 'tx_ms': 4801, 'crc': True}, {'seq': 6, 'rx_ms': 5200, 'tx_ms': 5050, 'crc': True}, {'seq': 6, 'rx_ms': 7700, 'tx_ms': 7550, 'crc': True}]}, [[True, True], [False, True], [False, True], [False, True], [False, True]]), ('control 18', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 251, 'rx_ms': 500, 'tx_ms': 201, 'crc': True}, {'seq': 252, 'rx_ms': 3000, 'tx_ms': 2701, 'crc': True}, {'seq': 254, 'rx_ms': 3500, 'tx_ms': 3199, 'crc': True}, {'seq': 254, 'rx_ms': 4000, 'tx_ms': 4020, 'crc': True}, {'seq': 0, 'rx_ms': 4500, 'tx_ms': 4201, 'crc': True}, {'seq': 0, 'rx_ms': 5000, 'tx_ms': 4400, 'crc': True}, {'seq': 4, 'rx_ms': 7500, 'tx_ms': 7199, 'crc': True}]}, [[True, True], [True, True], [False, True], [False, True], [False, True], [False, True], [False, True]])], [('regression: sequence wraps past 255', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 254, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 255, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 0, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [True, False], [True, False]]), ('sampled regression 66', {'timeout_ms': 2000, 'max_age_ms': 300, 'messages': [{'seq': 254, 'rx_ms': 200, 'tx_ms': 50, 'crc': True}, {'seq': 255, 'rx_ms': 400, 'tx_ms': 101, 'crc': True}, {'seq': 0, 'rx_ms': 2900, 'tx_ms': 2601, 'crc': True}, {'seq': 1, 'rx_ms': 3100, 'tx_ms': 3100, 'crc': True}, {'seq': 5, 'rx_ms': 3200, 'tx_ms': 3050, 'crc': True}, {'seq': 6, 'rx_ms': 3700, 'tx_ms': 3100, 'crc': True}, {'seq': 5, 'rx_ms': 3900, 'tx_ms': 3599, 'crc': True}, {'seq': 6, 'rx_ms': 5100, 'tx_ms': 4799, 'crc': True}]}, [[True, True], [True, False], [True, True], [True, False], [False, False], [False, False], [False, False], [False, False]]), ('control 48', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 253, 'rx_ms': 1200, 'tx_ms': 600, 'crc': True}, {'seq': 253, 'rx_ms': 2400, 'tx_ms': 2420, 'crc': True}, {'seq': 254, 'rx_ms': 4900, 'tx_ms': 4300, 'crc': True}, {'seq': 2, 'rx_ms': 5400, 'tx_ms': 5420, 'crc': True}, {'seq': 6, 'rx_ms': 5900, 'tx_ms': 5300, 'crc': True}, {'seq': 6, 'rx_ms': 6000, 'tx_ms': 5400, 'crc': True}, {'seq': 6, 'rx_ms': 8500, 'tx_ms': 8500, 'crc': True}, {'seq': 5, 'rx_ms': 8600, 'tx_ms': 8590, 'crc': True}]}, [[False, True], [False, True], [False, True], [False, True], [False, True], [False, True], [True, True], [False, True]]), ('boundary: telegram from the future', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 10, 'rx_ms': 100, 'tx_ms': 150, 'crc': True}, {'seq': 11, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 12, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[False, True], [True, True], [True, False]]), ('boundary: stale telegram', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 10, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 11, 'rx_ms': 800, 'tx_ms': 400, 'crc': True}, {'seq': 12, 'rx_ms': 900, 'tx_ms': 890, 'crc': True}]}, [[True, True], [False, True], [True, True]]), ('control 23', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 250, 'rx_ms': 2500, 'tx_ms': 2199, 'crc': True}, {'seq': 254, 'rx_ms': 3700, 'tx_ms': 3401, 'crc': True}, {'seq': 254, 'rx_ms': 3800, 'tx_ms': 3650, 'crc': True}, {'seq': 254, 'rx_ms': 4000, 'tx_ms': 3699, 'crc': True}, {'seq': 254, 'rx_ms': 6500, 'tx_ms': 6350, 'crc': True}, {'seq': 255, 'rx_ms': 6600, 'tx_ms': 6299, 'crc': True}, {'seq': 255, 'rx_ms': 9100, 'tx_ms': 9120, 'crc': True}, {'seq': 0, 'rx_ms': 11600, 'tx_ms': 11000, 'crc': True}, {'seq': 255, 'rx_ms': 12800, 'tx_ms': 12800, 'crc': True}]}, [[False, True], [True, True], [False, True], [False, True], [False, True], [False, True], [False, True], [False, True], [True, True]]), ('sampled regression 26', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 252, 'rx_ms': 1200, 'tx_ms': 899, 'crc': True}, {'seq': 253, 'rx_ms': 2400, 'tx_ms': 2250, 'crc': True}, {'seq': 254, 'rx_ms': 2900, 'tx_ms': 2890, 'crc': True}, {'seq': 254, 'rx_ms': 4100, 'tx_ms': 4090, 'crc': True}, {'seq': 253, 'rx_ms': 5300, 'tx_ms': 5290, 'crc': True}, {'seq': 0, 'rx_ms': 5400, 'tx_ms': 5390, 'crc': True}, {'seq': 1, 'rx_ms': 5500, 'tx_ms': 5201, 'crc': True}, {'seq': 3, 'rx_ms': 5600, 'tx_ms': 5000, 'crc': False}, {'seq': 2, 'rx_ms': 5700, 'tx_ms': 5720, 'crc': False}, {'seq': 3, 'rx_ms': 5800, 'tx_ms': 5501, 'crc': True}]}, [[False, True], [True, True], [True, False], [False, True], [False, True], [True, True], [True, False], [False, False], [False, False], [True, False]]), ('control 29', {'timeout_ms': 2000, 'max_age_ms': 300, 'messages': [{'seq': 255, 'rx_ms': 500, 'tx_ms': -100, 'crc': True}, {'seq': 3, 'rx_ms': 600, 'tx_ms': 600, 'crc': True}, {'seq': 6, 'rx_ms': 3100, 'tx_ms': 3090, 'crc': True}]}, [[False, True], [True, True], [True, True]])], [('regression: sequence wraps past 255', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 254, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 255, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 0, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [True, False], [True, False]]), ('boundary: replayed telegram', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 10, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 11, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 10, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [True, False], [False, False]]), ('sampled regression 27', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 254, 'rx_ms': 100, 'tx_ms': 120, 'crc': True}, {'seq': 255, 'rx_ms': 600, 'tx_ms': 600, 'crc': True}, {'seq': 2, 'rx_ms': 700, 'tx_ms': 550, 'crc': True}, {'seq': 2, 'rx_ms': 1900, 'tx_ms': 1599, 'crc': True}]}, [[False, True], [True, True], [True, False], [False, True]]), ('sampled regression 72', {'timeout_ms': 2000, 'max_age_ms': 300, 'messages': [{'seq': 254, 'rx_ms': 2500, 'tx_ms': 2500, 'crc': True}, {'seq': 253, 'rx_ms': 3700, 'tx_ms': 3720, 'crc': True}, {'seq': 254, 'rx_ms': 3900, 'tx_ms': 3920, 'crc': True}, {'seq': 254, 'rx_ms': 4000, 'tx_ms': 3990, 'crc': False}, {'seq': 253, 'rx_ms': 4200, 'tx_ms': 3600, 'crc': True}, {'seq': 253, 'rx_ms': 4400, 'tx_ms': 4390, 'crc': True}, {'seq': 1, 'rx_ms': 5600, 'tx_ms': 5300, 'crc': True}, {'seq': 1, 'rx_ms': 5700, 'tx_ms': 5690, 'crc': False}]}, [[True, True], [False, True], [False, True], [False, True], [False, True], [False, True], [True, True], [False, True]]), ('boundary: corrupt telegram between good ones', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 1, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 2, 'rx_ms': 200, 'tx_ms': 190, 'crc': False}, {'seq': 3, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}, {'seq': 4, 'rx_ms': 400, 'tx_ms': 390, 'crc': True}]}, [[True, True], [False, True], [True, True], [True, False]]), ('control 34', {'timeout_ms': 2000, 'max_age_ms': 300, 'messages': [{'seq': 255, 'rx_ms': 1200, 'tx_ms': 901, 'crc': False}, {'seq': 1, 'rx_ms': 1700, 'tx_ms': 1400, 'crc': True}, {'seq': 1, 'rx_ms': 4200, 'tx_ms': 4050, 'crc': True}, {'seq': 2, 'rx_ms': 4300, 'tx_ms': 4320, 'crc': True}]}, [[False, True], [True, True], [False, True], [False, True]]), ('control 37', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 251, 'rx_ms': 1200, 'tx_ms': 899, 'crc': True}, {'seq': 252, 'rx_ms': 1700, 'tx_ms': 1700, 'crc': True}, {'seq': 252, 'rx_ms': 2900, 'tx_ms': 2890, 'crc': True}, {'seq': 252, 'rx_ms': 3000, 'tx_ms': 3000, 'crc': True}, {'seq': 253, 'rx_ms': 3100, 'tx_ms': 2800, 'crc': True}, {'seq': 255, 'rx_ms': 4300, 'tx_ms': 4320, 'crc': True}]}, [[False, True], [True, True], [False, True], [False, True], [True, True], [False, True]]), ('control 40', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 254, 'rx_ms': 1200, 'tx_ms': 600, 'crc': True}, {'seq': 254, 'rx_ms': 2400, 'tx_ms': 2100, 'crc': True}, {'seq': 254, 'rx_ms': 2500, 'tx_ms': 2490, 'crc': False}]}, [[False, True], [True, True], [False, True]])], [('regression: sequence wraps past 255', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 254, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 255, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 0, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [True, False], [True, False]]), ('sampled regression 79', {'timeout_ms': 2000, 'max_age_ms': 300, 'messages': [{'seq': 252, 'rx_ms': 500, 'tx_ms': 201, 'crc': True}, {'seq': 255, 'rx_ms': 1700, 'tx_ms': 1550, 'crc': True}, {'seq': 255, 'rx_ms': 2900, 'tx_ms': 2600, 'crc': True}, {'seq': 2, 'rx_ms': 3100, 'tx_ms': 3100, 'crc': True}, {'seq': 2, 'rx_ms': 3600, 'tx_ms': 3590, 'crc': True}, {'seq': 6, 'rx_ms': 4800, 'tx_ms': 4500, 'crc': True}, {'seq': 7, 'rx_ms': 4900, 'tx_ms': 4890, 'crc': True}, {'seq': 7, 'rx_ms': 5400, 'tx_ms': 5390, 'crc': True}]}, [[True, True], [True, False], [False, False], [True, False], [False, False], [False, False], [False, False], [False, True]]), ('boundary: replayed telegram', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 10, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 11, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 10, 'rx_ms': 300, 'tx_ms': 290, 'crc': True}]}, [[True, True], [True, False], [False, False]]), ('boundary: timeout exactly reached', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 1, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 2, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 3, 'rx_ms': 1200, 'tx_ms': 1190, 'crc': True}]}, [[True, True], [True, False], [True, False]]), ('boundary: bad telegrams keep the link alive', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 1, 'rx_ms': 100, 'tx_ms': 90, 'crc': True}, {'seq': 2, 'rx_ms': 200, 'tx_ms': 190, 'crc': True}, {'seq': 9, 'rx_ms': 800, 'tx_ms': 790, 'crc': True}, {'seq': 3, 'rx_ms': 1100, 'tx_ms': 1090, 'crc': True}]}, [[True, True], [True, False], [False, False], [True, False]]), ('control 45', {'timeout_ms': 2000, 'max_age_ms': 300, 'messages': [{'seq': 13, 'rx_ms': 200, 'tx_ms': -100, 'crc': True}, {'seq': 14, 'rx_ms': 300, 'tx_ms': -1, 'crc': True}, {'seq': 18, 'rx_ms': 500, 'tx_ms': 201, 'crc': False}, {'seq': 19, 'rx_ms': 3000, 'tx_ms': 3020, 'crc': True}, {'seq': 17, 'rx_ms': 5500, 'tx_ms': 4900, 'crc': True}, {'seq': 16, 'rx_ms': 5700, 'tx_ms': 5401, 'crc': True}]}, [[True, True], [False, True], [False, True], [False, True], [False, True], [True, True]]), ('control 48', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 253, 'rx_ms': 1200, 'tx_ms': 600, 'crc': True}, {'seq': 253, 'rx_ms': 2400, 'tx_ms': 2420, 'crc': True}, {'seq': 254, 'rx_ms': 4900, 'tx_ms': 4300, 'crc': True}, {'seq': 2, 'rx_ms': 5400, 'tx_ms': 5420, 'crc': True}, {'seq': 6, 'rx_ms': 5900, 'tx_ms': 5300, 'crc': True}, {'seq': 6, 'rx_ms': 6000, 'tx_ms': 5400, 'crc': True}, {'seq': 6, 'rx_ms': 8500, 'tx_ms': 8500, 'crc': True}, {'seq': 5, 'rx_ms': 8600, 'tx_ms': 8590, 'crc': True}]}, [[False, True], [False, True], [False, True], [False, True], [False, True], [False, True], [True, True], [False, True]]), ('sampled regression 51', {'timeout_ms': 1000, 'max_age_ms': 300, 'messages': [{'seq': 251, 'rx_ms': 1200, 'tx_ms': 1190, 'crc': True}, {'seq': 252, 'rx_ms': 1400, 'tx_ms': 1099, 'crc': True}, {'seq': 253, 'rx_ms': 1900, 'tx_ms': 1920, 'crc': True}, {'seq': 253, 'rx_ms': 2400, 'tx_ms': 2400, 'crc': True}, {'seq': 254, 'rx_ms': 2600, 'tx_ms': 2301, 'crc': True}, {'seq': 0, 'rx_ms': 3100, 'tx_ms': 2801, 'crc': True}, {'seq': 2, 'rx_ms': 3300, 'tx_ms': 2999, 'crc': True}, {'seq': 3, 'rx_ms': 5800, 'tx_ms': 5499, 'crc': True}, {'seq': 4, 'rx_ms': 5900, 'tx_ms': 5601, 'crc': True}, {'seq': 5, 'rx_ms': 6000, 'tx_ms': 6020, 'crc': False}]}, [[True, True], [False, True], [False, True], [True, True], [True, False], [True, False], [False, False], [False, True], [False, True], [False, True]])]]
for label, args, expected in fixtures[N-1]:
check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: sequence wraps past 255 | [[True, True], [True, False], [True, False]] | [[True, True], [True, False], [True, False]] | Passed |
| sampled regression 35 | [[True, True], [True, False], [False, False], [True, False], [False, False], [False, True]] | [[True, True], [True, False], [False, False], [True, False], [False, False], [False, True]] | Passed |
| boundary: replayed telegram | [[True, True], [True, False], [False, False]] | [[True, True], [True, False], [False, False]] | Passed |
| boundary: duplicate telegram | [[True, True], [False, True], [True, True]] | [[True, True], [False, True], [True, True]] | Passed |
| boundary: two telegrams lost | [[True, True], [True, False], [True, False]] | [[True, True], [True, False], [True, False]] | Passed |
| control 1 | [[False, True], [False, True], [False, True], [False, True], [True, True]] | [[False, True], [False, True], [False, True], [False, True], [True, True]] | Passed |
| control 4 | [[True, True], [True, False], [True, False], [False, False], [True, False], [False, False], [False, False], [False, True], [False, True]] | [[True, True], [True, False], [True, False], [False, False], [True, False], [False, False], [False, False], [False, True], [False, True]] | Passed |
| control 7 | [[False, True], [True, True], [False, True], [False, True]] | [[False, True], [True, True], [False, True], [False, True]] | Passed |
SHA-256 / c4eb0d86b6b2980c20fe185e5012d447c05161a0beb1b85f15d3bc74e540bcf0
Verification & scope
Stipulated toy interlocking contract for a bounded teaching model; it makes no claim of conformance to any railway signalling standard and omits real safety cases. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:47:50.714901+00:00.
Case digest / 14087ba6a007a6db4060aa13fa0fa2dd5a718c5d08762f1ca678240258e9e0c9