fix(web): expose unattended planner heartbeat
This commit is contained in:
@@ -39,6 +39,14 @@ def main() -> int:
|
|||||||
"watchdogStatus": read_json(CONTROL_ROOT / "my-tools-watchdog.json"),
|
"watchdogStatus": read_json(CONTROL_ROOT / "my-tools-watchdog.json"),
|
||||||
"schedulerWrapperStatus": read_json(CONTROL_ROOT / "my-tools-scheduler.json"),
|
"schedulerWrapperStatus": read_json(CONTROL_ROOT / "my-tools-scheduler.json"),
|
||||||
}
|
}
|
||||||
|
watchdog = status["myTools"]["watchdogStatus"]
|
||||||
|
watchdog_pid = watchdog.get("pid") if isinstance(watchdog, dict) else None
|
||||||
|
watchdog_alive = isinstance(watchdog_pid, int) and (Path("/proc") / str(watchdog_pid)).exists()
|
||||||
|
status["myTools"]["watchdogAlive"] = watchdog_alive
|
||||||
|
status["unattendedState"] = (
|
||||||
|
"PLANNING" if status.get("state") == "IDLE" and watchdog_alive and watchdog.get("status") == "waiting_for_source_batch"
|
||||||
|
else status.get("state", "UNKNOWN")
|
||||||
|
)
|
||||||
status["progress"] = project_progress()
|
status["progress"] = project_progress()
|
||||||
status["fastPath"] = fast_path_status(status)
|
status["fastPath"] = fast_path_status(status)
|
||||||
if args.json:
|
if args.json:
|
||||||
@@ -53,7 +61,7 @@ def main() -> int:
|
|||||||
if args.watch:
|
if args.watch:
|
||||||
print("\x1b[2J\x1b[H", end="")
|
print("\x1b[2J\x1b[H", end="")
|
||||||
print("Blender Web/WBV2 scheduler")
|
print("Blender Web/WBV2 scheduler")
|
||||||
print(f" state: {status.get('state', 'UNKNOWN')}")
|
print(f" state: {status.get('unattendedState', status.get('state', 'UNKNOWN'))}")
|
||||||
print(f" phase: {status.get('phase', 'unknown')}")
|
print(f" phase: {status.get('phase', 'unknown')}")
|
||||||
queue = status.get("queue", {})
|
queue = status.get("queue", {})
|
||||||
print(f" current task: {queue.get('task') or 'none'}")
|
print(f" current task: {queue.get('task') or 'none'}")
|
||||||
@@ -65,12 +73,15 @@ def main() -> int:
|
|||||||
print(f" guardian: pid={guardian.get('pid') or 'none'} alive={guardian.get('alive', False)} state={guardian.get('state') or 'unknown'}")
|
print(f" guardian: pid={guardian.get('pid') or 'none'} alive={guardian.get('alive', False)} state={guardian.get('state') or 'unknown'}")
|
||||||
print(f" supervisor: pid={supervisor.get('pid') or 'none'} alive={supervisor.get('alive', False)} state={supervisor.get('state') or 'unknown'}")
|
print(f" supervisor: pid={supervisor.get('pid') or 'none'} alive={supervisor.get('alive', False)} state={supervisor.get('state') or 'unknown'}")
|
||||||
watchdog = status.get("myTools", {}).get("watchdogStatus", {})
|
watchdog = status.get("myTools", {}).get("watchdogStatus", {})
|
||||||
print(f" watchdog: pid={watchdog.get('pid') or 'external'} status={watchdog.get('status') or 'unknown'} reason={watchdog.get('reason') or 'none'} task={watchdog.get('task') or 'none'}")
|
print(f" watchdog: pid={watchdog.get('pid') or 'none'} alive={status.get('myTools', {}).get('watchdogAlive', False)} status={watchdog.get('status') or 'unknown'} reason={watchdog.get('reason') or 'none'} task={watchdog.get('task') or 'none'}")
|
||||||
print(f" latest handoff: {status.get('latestHandoff') or 'none'}")
|
print(f" latest handoff: {status.get('latestHandoff') or 'none'}")
|
||||||
print(f" local HEAD: {status.get('localHead') or 'unknown'}")
|
print(f" local HEAD: {status.get('localHead') or 'unknown'}")
|
||||||
for line in format_progress(status["progress"], status["fastPath"]):
|
for line in format_progress(status["progress"], status["fastPath"]):
|
||||||
print(line)
|
print(line)
|
||||||
print(f" message: {status.get('message', '')}")
|
if status.get("unattendedState") == "PLANNING":
|
||||||
|
print(" message: PLANNING: watchdog heartbeat is active; exact source gaps are queued without false coverage.")
|
||||||
|
else:
|
||||||
|
print(f" message: {status.get('message', '')}")
|
||||||
if not args.watch:
|
if not args.watch:
|
||||||
return 0
|
return 0
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -32,14 +32,17 @@ def now() -> str:
|
|||||||
|
|
||||||
def write_json(path: Path, value: dict) -> None:
|
def write_json(path: Path, value: dict) -> None:
|
||||||
path.parent.mkdir(parents=True, exist_ok=True)
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
try:
|
if path == WATCHDOG_STATUS:
|
||||||
current = json.loads(path.read_text(encoding="utf-8"))
|
value = {**value, "pid": os.getpid()}
|
||||||
stable_current = {key: item for key, item in current.items() if key != "updatedAt"}
|
else:
|
||||||
stable_next = {key: item for key, item in value.items() if key != "updatedAt"}
|
try:
|
||||||
if stable_current == stable_next:
|
current = json.loads(path.read_text(encoding="utf-8"))
|
||||||
return
|
stable_current = {key: item for key, item in current.items() if key != "updatedAt"}
|
||||||
except (OSError, json.JSONDecodeError):
|
stable_next = {key: item for key, item in value.items() if key != "updatedAt"}
|
||||||
pass
|
if stable_current == stable_next:
|
||||||
|
return
|
||||||
|
except (OSError, json.JSONDecodeError):
|
||||||
|
pass
|
||||||
temporary = path.with_suffix(path.suffix + f".tmp-{os.getpid()}")
|
temporary = path.with_suffix(path.suffix + f".tmp-{os.getpid()}")
|
||||||
temporary.write_text(json.dumps(value, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
temporary.write_text(json.dumps(value, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||||||
temporary.replace(path)
|
temporary.replace(path)
|
||||||
|
|||||||
Reference in New Issue
Block a user