Add Domains and Power rails views for functional groups.
Sidebar tabs show routing-first topology from placement_plan/functional_groups, with IC groups and satellite roles highlighted per domain and per supply rail. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -143,7 +143,12 @@ async def get_project(project_id: str, request: Request):
|
||||
storage = get_storage(request)
|
||||
owner_user_id, meta = await resolve_or_404(request, project_id)
|
||||
healed = proj_svc.heal_if_pipeline_finished(storage, owner_user_id, project_id)
|
||||
return (healed or meta).model_dump()
|
||||
if healed is not None:
|
||||
meta = healed
|
||||
healed_pl = proj_svc.heal_if_placement_stuck(storage, owner_user_id, project_id)
|
||||
if healed_pl is not None:
|
||||
meta = healed_pl
|
||||
return meta.model_dump()
|
||||
|
||||
|
||||
@router.delete("/projects/{project_id}")
|
||||
|
||||
@@ -1551,9 +1551,11 @@ def _write_functional_groups(ws: PipelineWorkspace, graph) -> None:
|
||||
if extracted_dir.is_dir():
|
||||
cmap = _build_constraints_map(_load_datasheets(extracted_dir))
|
||||
report = build_functional_groups(graph, cmap)
|
||||
out = ws.local_path("functional_groups.json")
|
||||
out.write_text(report.model_dump_json(indent=2) + "\n")
|
||||
ws._upload_file("functional_groups.json")
|
||||
payload = report.model_dump_json(indent=2) + "\n"
|
||||
for name in ("functional_groups.json", "placement_plan.json"):
|
||||
out = ws.local_path(name)
|
||||
out.write_text(payload)
|
||||
ws._upload_file(name)
|
||||
except Exception:
|
||||
logger.exception("functional_groups.json write failed — continuing")
|
||||
|
||||
|
||||
@@ -326,6 +326,77 @@ def heal_if_pipeline_finished(
|
||||
return None
|
||||
|
||||
|
||||
def heal_if_placement_stuck(
|
||||
storage: StorageBackend, user_id: str, project_id: str,
|
||||
) -> ProjectMeta | None:
|
||||
"""Unstick placement_status queued/running when the worker is gone.
|
||||
|
||||
- Last event ``placement_complete`` → ``complete``
|
||||
- Dead worker + plan artifact present → ``complete``
|
||||
- Dead worker otherwise → ``error``
|
||||
"""
|
||||
meta = get_project(storage, user_id, project_id)
|
||||
if meta is None:
|
||||
return None
|
||||
pst = meta.placement_status or "draft"
|
||||
if pst not in ("queued", "running"):
|
||||
return None
|
||||
|
||||
prefix = _project_prefix(user_id, project_id)
|
||||
events_prefix = f"{prefix}/events/"
|
||||
last_event = None
|
||||
try:
|
||||
keys = sorted(
|
||||
k for k in storage.list_prefix(events_prefix)
|
||||
if k.endswith(".json") and "/events/" in k
|
||||
)
|
||||
if keys:
|
||||
last_event = storage.read_json(keys[-1])
|
||||
except Exception:
|
||||
last_event = None
|
||||
|
||||
if (last_event or {}).get("event") == "placement_complete":
|
||||
data = (last_event or {}).get("data") or {}
|
||||
return update_project(
|
||||
storage, user_id, project_id,
|
||||
placement_status="complete",
|
||||
placement_cancel_requested=False,
|
||||
placement_state={
|
||||
"domains": data.get("domains"),
|
||||
"groups": data.get("groups"),
|
||||
},
|
||||
)
|
||||
|
||||
from backend.services import job_runner
|
||||
|
||||
exec_name = meta.placement_execution_name or f"local/placement/{project_id}"
|
||||
try:
|
||||
state = job_runner.get_execution_state(exec_name)
|
||||
except Exception:
|
||||
state = "unknown"
|
||||
|
||||
if state in ("pending", "running"):
|
||||
return None
|
||||
|
||||
has_plan = (
|
||||
storage.exists(f"{prefix}/placement_plan.json")
|
||||
or storage.exists(f"{prefix}/functional_groups.json")
|
||||
)
|
||||
if has_plan:
|
||||
return update_project(
|
||||
storage, user_id, project_id,
|
||||
placement_status="complete",
|
||||
placement_cancel_requested=False,
|
||||
placement_state=meta.placement_state,
|
||||
)
|
||||
return update_project(
|
||||
storage, user_id, project_id,
|
||||
placement_status="error",
|
||||
placement_cancel_requested=False,
|
||||
placement_state={"error": f"Placement worker terminated ({state})"},
|
||||
)
|
||||
|
||||
|
||||
# --- CRUD ---
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user