feat(task-6): door, window and room schedules read from the drawing

engineering/arch/schedule.py turns the plan records into schedule rows: doors
by tag with width, height, swing, hand and wall; windows by tag with width,
height, sill and wall; rooms by number with name and the measured area the
label carries, plus a total. Tags compare as numbers (D2 before D10). An
opening without a tag gets the next free D1 / W1 - K1 / P1 in Turkish - in
wall order; an explicit tag is never renumbered and a generated one never
repeats it. A value the model does not carry is an empty cell, never a default,
and Turkish output writes decimal commas.

draw_schedule reads the ACADMCP_ARCH records off the drawing through the one
reader rooms.py already has and draws a real TABLE the way the ISO 7573 parts
list does - top-left insertion, the table growing down, the parts list's 7 mm
rows and 2.5 mm text at the plot scale, the engine's representation reported.
Nothing to schedule, an unknown kind or language and more rows than one TABLE
takes are refused before anything is drawn.

arch_schedule joins arch_room and arch_rooms_detect under SECTION 25's rooms
marker (banner 3 tools), shares the TABLE command with entity_create_table and
bom_table, and the counts follow: 232 tools in 25 groups.

Live fix in com_backend.entity_create_table. MEASURED on AutoCAD 2026: IAcadTable
has no TextHeight property. pywin32 raised AttributeError on
`table.TextHeight = h`, the old code swallowed it, and every live table kept
the Standard style's heights (title 6.0, header and data 4.5), which is 0.1 mm
on paper at 1:50. The member that works is SetCellTextHeight(row, col, h),
probed on a scratch document: every cell read back 125.0 via
GetCellTextHeight. The row heights stayed 350.0 and GetBoundingBox matched the
reported bbox (10000,3250)-(15750,5000). Then draw_schedule ran end to end
through ComBackend on a scratch document: a native TABLE on A-SYMB-T-N for
doors (5 rows) and rooms/tr (6 rows, total 45,00 m²), and the document was
closed afterwards. The new fake in tests/test_table_leader.py has only the
measured members.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd
This commit is contained in:
Umut CAN
2026-09-23 22:56:05 +03:00
co-authored by Claude Opus 5.5
parent a2996b0c95
commit 9168d61397
11 changed files with 719 additions and 21 deletions
+44 -1
View File
@@ -9781,7 +9781,7 @@ async def drawing_export_dwg(
# ---------------------------------------------------------------------------
# ── SECTION 25: Architecture (2 tools) ──────────────────────────────────────
# ── SECTION 25: Architecture (3 tools) ──────────────────────────────────────
# ---------------------------------------------------------------------------
@@ -9899,6 +9899,49 @@ async def arch_rooms_detect(
raise ToolError(str(exc)) from exc
@cad_tool(summary="Draw a door, window or room schedule from the plan records.", cost="mutate")
@mcp.tool(
annotations={"title": "Architecture: Schedule", "destructiveHint": False},
tags={"arch", "create"},
)
async def arch_schedule(
kind: Annotated[str, Field(description="doors | windows | rooms")],
x: Annotated[float, Field(description="WCS X of the table's top-left corner.")],
y: Annotated[float, Field(description="WCS Y of the table's top-left corner.")],
lang: Annotated[
str, Field(default="en", description="Headers and numbers: en | tr (decimal comma).")
] = "en",
scale: Annotated[
float,
Field(default=50.0, gt=0, description="Plot-scale denominator: 50 is 1:50."),
] = 50.0,
ctx: Context = None,
) -> dict:
"""Draw a door, window or room schedule as a real TABLE - a read of the drawing.
The rows come from the `ACADMCP_ARCH` records on the drawing (what
`arch_wall` / `arch_opening` / `arch_room` write), never from the caller:
doors by tag with width, height, swing, hand and wall; windows by tag with
width, height, sill and wall; rooms by number with name and the measured
area, plus a total. An opening drawn without a tag gets the next free D1 /
W1 (K1 / P1 in Turkish) in wall order; an explicit tag is kept. A value the
model does not carry is an empty cell, never a default. `representation`
reports what was drawn: `native` (ACAD_TABLE, live) or `composite` (rules
and MTEXT, headless).
Refused before anything is drawn: an unknown `kind` or `lang` (named with
the list), nothing of that kind on the drawing, and more rows than one
TABLE takes (198).
"""
from engineering.arch.schedule import draw_schedule
await ctx.info(f"arch_schedule: {kind} at ({x}, {y})")
try:
return await draw_schedule(_backend(ctx), kind, at=(x, y), lang=lang, scale=scale)
except ValueError as exc:
raise ToolError(str(exc)) from exc
# ---------------------------------------------------------------------------
# ── RESOURCES ───────────────────────────────────────────────────────────────
# ---------------------------------------------------------------------------