Harden RF handoff and stream OTA decode
This commit is contained in:
@@ -23,6 +23,8 @@ if str(ROOT) not in sys.path:
|
||||
from tools.rf_handoff_channel import ( # noqa: E402
|
||||
EXPECTED_RX_TASK_ID,
|
||||
GitResultChannel,
|
||||
READY_STATE,
|
||||
RxHandoffResult,
|
||||
load_result,
|
||||
)
|
||||
from tools.tx_handoff_channel import ( # noqa: E402
|
||||
@@ -571,6 +573,7 @@ def publish_result(arguments: argparse.Namespace) -> int:
|
||||
task = parse_task((repository / DEFAULT_TASK_PATH).read_text(encoding="utf-8-sig"))
|
||||
state = load_state(arguments.state_file.resolve())
|
||||
result = load_result(arguments.publish_result.resolve())
|
||||
require_live_ready_window(result)
|
||||
if task.task_id != result.task_id:
|
||||
raise ValueError("result TASK_ID disagrees with current RX task")
|
||||
if task.allow_push != "RX_RESULT_ONLY" or task.rf_tx:
|
||||
@@ -613,10 +616,55 @@ def publish_result(arguments: argparse.Namespace) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
def require_live_ready_window(
|
||||
result: RxHandoffResult,
|
||||
*,
|
||||
now: datetime | None = None,
|
||||
) -> None:
|
||||
"""Запретить READY после окончания конечного окна захвата."""
|
||||
|
||||
if result.state != READY_STATE:
|
||||
return
|
||||
current = (now or datetime.now(timezone.utc)).astimezone(timezone.utc)
|
||||
expected_end = datetime.fromisoformat(
|
||||
result.expected_capture_end.replace("Z", "+00:00")
|
||||
).astimezone(timezone.utc)
|
||||
if current >= expected_end:
|
||||
raise ValueError("READY capture window has already ended")
|
||||
|
||||
|
||||
def verify_result_channel_write_access(
|
||||
repository: Path,
|
||||
*,
|
||||
remote: str = "origin",
|
||||
result_branch: str = "rx-node",
|
||||
output_function: Callable[[str], None] = print,
|
||||
) -> str:
|
||||
"""Проверить право публикации результата RX без изменения ветки."""
|
||||
|
||||
channel = GitResultChannel(
|
||||
repository,
|
||||
remote=remote,
|
||||
branch=result_branch,
|
||||
)
|
||||
remote_head = channel.verify_write_access()
|
||||
output_function("RX-NODE WRITE PREFLIGHT = PASS")
|
||||
output_function(f"RX-NODE REMOTE HEAD = {remote_head}")
|
||||
output_function("RF TX = 0")
|
||||
return remote_head
|
||||
|
||||
|
||||
def run_watcher(arguments: argparse.Namespace) -> int:
|
||||
state_path = arguments.state_file.resolve()
|
||||
if arguments.publish_result is not None:
|
||||
return publish_result(arguments)
|
||||
if arguments.check_result_write:
|
||||
verify_result_channel_write_access(
|
||||
arguments.repository.resolve(),
|
||||
remote=arguments.remote,
|
||||
result_branch=arguments.result_branch,
|
||||
)
|
||||
return 0
|
||||
if arguments.request_stop:
|
||||
stopped = request_stop(state_path, arguments.stop_timeout)
|
||||
print("WATCHER STOPPED" if stopped else "WATCHER STOP TIMEOUT")
|
||||
@@ -670,6 +718,12 @@ def run_watcher(arguments: argparse.Namespace) -> int:
|
||||
task_text, source_head = source.read()
|
||||
task = parse_task(task_text)
|
||||
if task.task_id == EXPECTED_RX_TASK_ID and not arguments.dry_run:
|
||||
if is_new_task(task, state):
|
||||
verify_result_channel_write_access(
|
||||
arguments.repository.resolve(),
|
||||
remote=arguments.remote,
|
||||
result_branch=arguments.result_branch,
|
||||
)
|
||||
confirm_main_channel_test(
|
||||
arguments.repository.resolve(),
|
||||
state,
|
||||
@@ -726,6 +780,7 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
parser.add_argument("--stop-timeout", type=float, default=35.0)
|
||||
parser.add_argument("--mark-completed")
|
||||
parser.add_argument("--publish-result", type=Path)
|
||||
parser.add_argument("--check-result-write", action="store_true")
|
||||
parser.add_argument("--result-branch", default="rx-node")
|
||||
parser.add_argument("--acknowledgement-branch", default="tx-node")
|
||||
return parser
|
||||
|
||||
Reference in New Issue
Block a user