juc500 정상동작
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
"""Dev-only: push local project sources to the USB peer (manual sync_peer.sh)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, Optional
|
||||
|
||||
from .protocol import Session
|
||||
|
||||
_ROOT = Path(__file__).resolve().parent.parent
|
||||
|
||||
# Keep in sync with scripts/sync_peer.sh default set.
|
||||
SYNC_XFER = (
|
||||
"juc500_xfer/gui_server.py",
|
||||
"juc500_xfer/usb_link.py",
|
||||
"juc500_xfer/inputshare.py",
|
||||
"juc500_xfer/protocol.py",
|
||||
"juc500_xfer/peer_sync.py",
|
||||
)
|
||||
SYNC_STATIC = (
|
||||
"juc500_xfer/static/app.js",
|
||||
"juc500_xfer/static/index.html",
|
||||
"juc500_xfer/static/styles.css",
|
||||
)
|
||||
|
||||
|
||||
def _use_mac_peer_paths() -> bool:
|
||||
return os.environ.get("USE_MAC_PEER_PATHS", "").strip() in ("1", "true", "yes")
|
||||
|
||||
|
||||
def peer_dest_dirs() -> dict[str, str]:
|
||||
"""Destination directories on the peer filesystem."""
|
||||
if _use_mac_peer_paths():
|
||||
base = "dev/juc500/juc500"
|
||||
return {
|
||||
"xfer": f"{base}/juc500_xfer",
|
||||
"static": f"{base}/juc500_xfer/static",
|
||||
}
|
||||
root = os.environ.get("JUC500_PEER_ROOT", "C:/dev/juc500").replace("\\", "/").rstrip("/")
|
||||
alt = os.environ.get("JUC500_PEER_ROOT_ALT", "C:/dev/juc500/juc500").replace("\\", "/").rstrip("/")
|
||||
return {
|
||||
"xfer": f"{root}/juc500_xfer",
|
||||
"static": f"{root}/juc500_xfer/static",
|
||||
"xfer_alt": f"{alt}/juc500_xfer",
|
||||
"static_alt": f"{alt}/juc500_xfer/static",
|
||||
}
|
||||
|
||||
|
||||
def _local_paths(rel_paths: tuple[str, ...]) -> list[Path]:
|
||||
out: list[Path] = []
|
||||
for rel in rel_paths:
|
||||
p = (_ROOT / rel).resolve()
|
||||
if p.is_file():
|
||||
out.append(p)
|
||||
return out
|
||||
|
||||
|
||||
def _send_batch(
|
||||
session: Session,
|
||||
dest_dir: str,
|
||||
files: list[Path],
|
||||
*,
|
||||
on_log: Callable[[str], None],
|
||||
on_progress: Optional[Callable[[int, int, str], None]] = None,
|
||||
) -> int:
|
||||
if not files:
|
||||
return 0
|
||||
dest = dest_dir.replace("\\", "/").rstrip("/")
|
||||
count = 0
|
||||
for local in files:
|
||||
remote_name = f"{dest}/{local.name}" if dest else local.name
|
||||
size = local.stat().st_size
|
||||
if on_progress:
|
||||
on_progress(0, size, local.name)
|
||||
session.on_progress = (
|
||||
(lambda done, total, name=local.name: on_progress(done, total, name))
|
||||
if on_progress
|
||||
else None
|
||||
)
|
||||
timeout = 180.0 if size > 32 * 1024 * 1024 else 120.0
|
||||
session.send_file(local, remote_name=remote_name, accept_timeout=timeout)
|
||||
count += 1
|
||||
on_log(f"상대 동기화: {local.name} → {dest}")
|
||||
return count
|
||||
|
||||
|
||||
def peer_ping(session: Session, *, timeout: float = 10.0) -> dict[str, Any]:
|
||||
try:
|
||||
result = session.rpc("ping", timeout=timeout)
|
||||
return result if isinstance(result, dict) else {}
|
||||
except Exception:
|
||||
return {}
|
||||
|
||||
|
||||
def local_build_ids() -> tuple[str, str]:
|
||||
try:
|
||||
from .inputshare import INPUTSHARE_BUILD
|
||||
|
||||
inputshare = INPUTSHARE_BUILD
|
||||
except Exception:
|
||||
inputshare = "unknown"
|
||||
try:
|
||||
from .usb_link import XFER_BUILD
|
||||
|
||||
xfer = XFER_BUILD
|
||||
except Exception:
|
||||
xfer = "unknown"
|
||||
return inputshare, xfer
|
||||
|
||||
|
||||
def peer_needs_restart(session: Session) -> bool:
|
||||
ping = peer_ping(session)
|
||||
local_inputshare, local_xfer = local_build_ids()
|
||||
peer_inputshare = str(ping.get("inputshare_build") or "unknown")
|
||||
peer_xfer = str(ping.get("xfer_build") or "unknown")
|
||||
if peer_xfer == "unknown":
|
||||
return True
|
||||
return peer_inputshare != local_inputshare or peer_xfer != local_xfer
|
||||
|
||||
|
||||
def request_peer_restart(session: Session, *, on_log: Callable[[str], None]) -> bool:
|
||||
try:
|
||||
session.rpc("restart_gui", timeout=12.0)
|
||||
on_log("상대편 GUI 재시작 예약됨 — 프로세스가 새 코드를 로드합니다")
|
||||
return True
|
||||
except Exception as exc:
|
||||
on_log(f"상대 재시작 RPC 실패: {exc} — 상대 PC에서 ./start.sh 수동 실행 필요")
|
||||
return False
|
||||
|
||||
|
||||
def push_sources_only(
|
||||
session: Session,
|
||||
*,
|
||||
on_log: Callable[[str], None],
|
||||
on_progress: Optional[Callable[[int, int, str], None]] = None,
|
||||
) -> int:
|
||||
xfer_files = _local_paths(SYNC_XFER)
|
||||
static_files = _local_paths(SYNC_STATIC)
|
||||
dests = peer_dest_dirs()
|
||||
total = 0
|
||||
for key in ("xfer", "static"):
|
||||
files = xfer_files if key == "xfer" else static_files
|
||||
dest = dests.get(key)
|
||||
if dest:
|
||||
total += _send_batch(session, dest, files, on_log=on_log, on_progress=on_progress)
|
||||
for key, alt_key in (("xfer", "xfer_alt"), ("static", "static_alt")):
|
||||
alt = dests.get(alt_key)
|
||||
if not alt or alt == dests.get(key):
|
||||
continue
|
||||
files = xfer_files if key == "xfer" else static_files
|
||||
total += _send_batch(session, alt, files, on_log=on_log, on_progress=on_progress)
|
||||
return total
|
||||
|
||||
|
||||
def sync_sources_to_peer(
|
||||
session: Session,
|
||||
*,
|
||||
on_log: Callable[[str], None],
|
||||
on_progress: Optional[Callable[[int, int, str], None]] = None,
|
||||
restart_if_stale: bool = False,
|
||||
) -> dict[str, Any]:
|
||||
"""Send default source bundle to peer. Optionally restart peer to load new code."""
|
||||
on_log("상대편 소스 자동 동기화 시작…")
|
||||
total = push_sources_only(session, on_log=on_log, on_progress=on_progress)
|
||||
if total <= 0:
|
||||
return {"ok": False, "error": "동기화할 로컬 파일 없음", "count": 0}
|
||||
|
||||
local_inputshare, local_xfer = local_build_ids()
|
||||
ping = peer_ping(session)
|
||||
peer_inputshare = str(ping.get("inputshare_build") or "unknown")
|
||||
peer_xfer = str(ping.get("xfer_build") or "unknown")
|
||||
restart = False
|
||||
if restart_if_stale and (
|
||||
peer_xfer != local_xfer or peer_inputshare != local_inputshare
|
||||
):
|
||||
on_log(
|
||||
f"상대 build 불일치 (xfer 로컬 {local_xfer} · 상대 {peer_xfer}) — 재시작 요청"
|
||||
)
|
||||
restart = request_peer_restart(session, on_log=on_log)
|
||||
|
||||
on_log(f"상대편 소스 동기화 완료 ({total}개 파일)")
|
||||
return {
|
||||
"ok": True,
|
||||
"count": total,
|
||||
"local_xfer_build": local_xfer,
|
||||
"peer_xfer_build": peer_xfer,
|
||||
"restart_requested": restart,
|
||||
}
|
||||
Reference in New Issue
Block a user