Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd
94 lines
2.8 KiB
Python
94 lines
2.8 KiB
Python
"""``ACADMCP_PID`` XDATA payloads.
|
|
|
|
XDATA strings are capped at 255 characters, so a JSON payload is stored as
|
|
``["json", chunk, chunk, ...]`` and re-joined on read. Every symbol INSERT and
|
|
every P&ID line carries one, so a reader without the catalogue (another tool,
|
|
a later catalogue version) still sees ports, classes and numbers.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from typing import TYPE_CHECKING, Any
|
|
|
|
if TYPE_CHECKING:
|
|
from backends.base import AutoCADBackend
|
|
|
|
APP_NAME = "ACADMCP_PID"
|
|
MARKER = "json"
|
|
CHUNK = 255
|
|
PAYLOAD_VERSION = 1
|
|
|
|
|
|
def encode_payload(payload: dict) -> list[str]:
|
|
text = json.dumps(payload, separators=(",", ":"), sort_keys=True, ensure_ascii=True)
|
|
return [MARKER] + [text[i : i + CHUNK] for i in range(0, len(text), CHUNK)]
|
|
|
|
|
|
def decode_payload(values: list) -> dict | None:
|
|
if not values or values[0] != MARKER:
|
|
return None
|
|
parts = [v for v in values[1:] if isinstance(v, str)]
|
|
try:
|
|
decoded = json.loads("".join(parts))
|
|
except json.JSONDecodeError:
|
|
return None
|
|
return decoded if isinstance(decoded, dict) else None
|
|
|
|
|
|
async def write_payload(backend: AutoCADBackend, handle: str, payload: dict) -> dict:
|
|
return await backend.entity_set_xdata(handle, APP_NAME, encode_payload(payload))
|
|
|
|
|
|
async def read_payload(backend: AutoCADBackend, handle: str) -> dict | None:
|
|
result = await backend.entity_get_xdata(handle, APP_NAME)
|
|
return decode_payload(result.get("xdata", {}).get(APP_NAME, []))
|
|
|
|
|
|
def symbol_payload(spec: Any, params: dict | None = None) -> dict:
|
|
"""``spec`` is an ``engineering.pid.symbols.SymbolSpec`` (typed loosely to
|
|
avoid an import cycle). Ports are symbol-local ``[x, y, direction, kind, radius]``."""
|
|
from .symbols import CATALOG_VERSION
|
|
|
|
return {
|
|
"v": PAYLOAD_VERSION,
|
|
"kind": "symbol",
|
|
"catalog": CATALOG_VERSION,
|
|
"name": spec.name,
|
|
"family": spec.family,
|
|
"symbol": spec.symbol,
|
|
"variant": spec.variant,
|
|
"params": dict(params or spec.params),
|
|
"ports": {p.name: [p.x, p.y, p.direction_deg, p.kind, p.radius] for p in spec.ports},
|
|
}
|
|
|
|
|
|
def line_payload(
|
|
line_class: str,
|
|
number: str | None,
|
|
size: str | None,
|
|
service: str | None,
|
|
spec: str | None,
|
|
insulation: str | None,
|
|
seq: int | None,
|
|
from_ref: dict | None,
|
|
to_ref: dict | None,
|
|
) -> dict:
|
|
return {
|
|
"v": PAYLOAD_VERSION,
|
|
"kind": "line",
|
|
"class": line_class,
|
|
"number": number,
|
|
"size": size,
|
|
"service": service,
|
|
"spec": spec,
|
|
"insulation": insulation,
|
|
"seq": seq,
|
|
"from": from_ref,
|
|
"to": to_ref,
|
|
}
|
|
|
|
|
|
def marker_payload(line_handle: str) -> dict:
|
|
return {"v": PAYLOAD_VERSION, "kind": "marker", "line": line_handle}
|