fix
This commit is contained in:
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"cmake.sourceDirectory": "/home/awe/Documents/radar_system/data_acq_and_processing/gpr_cpp"
|
||||||
|
}
|
||||||
+2
-1
@@ -5,6 +5,7 @@
|
|||||||
"platformio.platformio-ide"
|
"platformio.platformio-ide"
|
||||||
],
|
],
|
||||||
"unwantedRecommendations": [
|
"unwantedRecommendations": [
|
||||||
"ms-vscode.cpptools-extension-pack"
|
"ms-vscode.cpptools-extension-pack",
|
||||||
|
"pioarduino.pioarduino-ide"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ handshake-кадром IDENT (роль + версия протокола). Дл
|
|||||||
import argparse
|
import argparse
|
||||||
import hmac
|
import hmac
|
||||||
import logging
|
import logging
|
||||||
|
import select
|
||||||
import socket
|
import socket
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
@@ -128,7 +129,13 @@ def run_tx(args, key):
|
|||||||
log.info("tx: %s -> UDP %s:%d", ser.port, *peer)
|
log.info("tx: %s -> UDP %s:%d", ser.port, *peer)
|
||||||
sent = 0
|
sent = 0
|
||||||
while True:
|
while True:
|
||||||
for ftype, payload in parser.feed(ser.read(256)):
|
# read(1) блокирует до первого байта, затем забираем всё доступное —
|
||||||
|
# кадр уходит в сеть сразу, без ожидания таймаута чтения (иначе каждый
|
||||||
|
# кадр задерживался бы до таймаута порта).
|
||||||
|
data = ser.read(1)
|
||||||
|
if ser.in_waiting:
|
||||||
|
data += ser.read(ser.in_waiting)
|
||||||
|
for ftype, payload in parser.feed(data):
|
||||||
if ftype == lp.CONTROL:
|
if ftype == lp.CONTROL:
|
||||||
sock.sendto(seal(payload, key), peer)
|
sock.sendto(seal(payload, key), peer)
|
||||||
sent += 1
|
sent += 1
|
||||||
@@ -149,7 +156,7 @@ def run_rx(args, key):
|
|||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
sock.bind(bind)
|
sock.bind(bind)
|
||||||
sock.settimeout(lp.FRAME_PERIOD_MS / 1000.0)
|
sock.setblocking(False)
|
||||||
parser = lp.FrameParser()
|
parser = lp.FrameParser()
|
||||||
log.info("rx: UDP %s:%d -> %s", bind[0], bind[1], ser.port)
|
log.info("rx: UDP %s:%d -> %s", bind[0], bind[1], ser.port)
|
||||||
|
|
||||||
@@ -160,37 +167,55 @@ def run_rx(args, key):
|
|||||||
in_failsafe = False
|
in_failsafe = False
|
||||||
rejects = 0 # подряд отброшенных по seq -> признак перезапуска источника
|
rejects = 0 # подряд отброшенных по seq -> признак перезапуска источника
|
||||||
|
|
||||||
while True:
|
def accept(datagram, addr):
|
||||||
try:
|
"""Проверить пакет и обновить состояние; вернуть payload принятого
|
||||||
datagram, addr = sock.recvfrom(2048)
|
свежего кадра либо None."""
|
||||||
|
nonlocal last_seq, last_net, in_failsafe, rejects
|
||||||
if allowed_host and addr[0] != allowed_host:
|
if allowed_host and addr[0] != allowed_host:
|
||||||
log.warning("пакет с неожиданного адреса %s отброшен", addr[0])
|
log.warning("пакет с неожиданного адреса %s отброшен", addr[0])
|
||||||
datagram = None
|
return None
|
||||||
payload = unseal(datagram, key) if datagram else None
|
payload = unseal(datagram, key)
|
||||||
if payload and len(payload) == lp.CONTROL_LEN:
|
if not payload or len(payload) != lp.CONTROL_LEN:
|
||||||
|
log.warning("битый/неаутентичный пакет отброшен")
|
||||||
|
return None
|
||||||
c = lp.unpack_control(payload)
|
c = lp.unpack_control(payload)
|
||||||
# Принять свежий seq ИЛИ новую сессию: источник (STM-TX) после
|
# Принять свежий seq ИЛИ новую сессию: источник (STM-TX) после ребута
|
||||||
# ребута начинает seq заново с 0, и без сброса last_seq поток
|
# начинает seq заново с 0, и без сброса last_seq поток завис бы в
|
||||||
# завис бы в failsafe навсегда. Новую сессию распознаём по пачке
|
# failsafe навсегда. Новую сессию распознаём по пачке подряд «несвежих»
|
||||||
# подряд «несвежих» пакетов (устойчиво к одиночным reorder/dup).
|
# пакетов (устойчиво к одиночным reorder/dup).
|
||||||
fresh = last_seq is None or seq_newer(c["seq"], last_seq)
|
fresh = last_seq is None or seq_newer(c["seq"], last_seq)
|
||||||
if not fresh:
|
if not fresh:
|
||||||
rejects += 1
|
rejects += 1
|
||||||
if rejects >= SEQ_RESET_AFTER:
|
if rejects >= SEQ_RESET_AFTER:
|
||||||
log.warning("seq откатился (%d -> %d): новая сессия", last_seq, c["seq"])
|
log.warning("seq откатился (%d -> %d): новая сессия", last_seq, c["seq"])
|
||||||
fresh = True
|
fresh = True
|
||||||
if fresh:
|
if not fresh:
|
||||||
|
return None # устаревший/повторный — anti-replay
|
||||||
rejects = 0
|
rejects = 0
|
||||||
last_seq = c["seq"]
|
last_seq = c["seq"]
|
||||||
last_net = time.monotonic()
|
last_net = time.monotonic()
|
||||||
if in_failsafe:
|
if in_failsafe:
|
||||||
log.info("связь восстановлена")
|
log.info("связь восстановлена")
|
||||||
in_failsafe = False
|
in_failsafe = False
|
||||||
ser.write(lp.build_frame(lp.CONTROL, payload))
|
return payload
|
||||||
elif datagram is not None:
|
|
||||||
log.warning("битый/неаутентичный пакет отброшен")
|
while True:
|
||||||
except socket.timeout:
|
# Ждём пакет до 20 мс (пауза цикла), затем БЕЗ блокировки выгребаем всю
|
||||||
pass
|
# накопившуюся очередь сокета и шлём на STM только самый свежий кадр
|
||||||
|
# (latest-wins): так задержка не растёт, даже если пакеты пришли пачкой.
|
||||||
|
newest = None
|
||||||
|
ready = select.select([sock], [], [], lp.FRAME_PERIOD_MS / 2000.0)[0]
|
||||||
|
while ready:
|
||||||
|
try:
|
||||||
|
datagram, addr = sock.recvfrom(2048)
|
||||||
|
except BlockingIOError:
|
||||||
|
break
|
||||||
|
p = accept(datagram, addr)
|
||||||
|
if p is not None:
|
||||||
|
newest = p
|
||||||
|
ready = select.select([sock], [], [], 0)[0]
|
||||||
|
if newest is not None:
|
||||||
|
ser.write(lp.build_frame(lp.CONTROL, newest))
|
||||||
|
|
||||||
# сетевой failsafe: тишина -> активная нейтраль на STM в темпе 20 Гц
|
# сетевой failsafe: тишина -> активная нейтраль на STM в темпе 20 Гц
|
||||||
now = time.monotonic()
|
now = time.monotonic()
|
||||||
@@ -203,8 +228,9 @@ def run_rx(args, key):
|
|||||||
fs_seq = (fs_seq + 1) & 0xFFFFFFFF
|
fs_seq = (fs_seq + 1) & 0xFFFFFFFF
|
||||||
last_fs = now
|
last_fs = now
|
||||||
|
|
||||||
# дренируем входящие LOG/IDENT от STM-RX
|
# неблокирующий дренаж входящих LOG от STM-RX
|
||||||
for ftype, payload in parser.feed(ser.read(256)):
|
if ser.in_waiting:
|
||||||
|
for ftype, payload in parser.feed(ser.read(ser.in_waiting)):
|
||||||
if ftype == lp.LOG:
|
if ftype == lp.LOG:
|
||||||
log.info("STM-RX: %s", payload.decode("ascii", "replace"))
|
log.info("STM-RX: %s", payload.decode("ascii", "replace"))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user