Files
kompas3d-mcp/docs/superpowers/specs/2026-05-27-drawing-title-block-design.md
T
mikhail 742a97177b feat: drawing_fill_title_block — основная надпись чертежа (API7)
Веха 2D-ЧЕРТЁЖ, инкремент 2 (оформление). DrawingService.FillTitleBlockAsync:
LayoutSheets.ItemByNumber[1].Stamp.Text[id].Str = text → Update(). Enum StampField
→ номер графы ГОСТ (designation=2, name=1, material=3). Заполнение нескольких
граф за вызов; перезапись (Str замещает, Clear не нужен — подтверждено спайком).
Новый инструмент drawing_fill_title_block; ReadStampCellAsync для проверки.

Ревью Codex спека учтено: Update() результат проверяется; null-checks
(LayoutSheets/sheet/Stamp); RequireActiveDrawing выделен (сохраняет cast-проверку);
ColumnId switch с дефолтным throw; счётчик = непустые поля; name=графа 1 однозначно.

Валидация — чистый StampFields (ColumnId + Collect). Тесты: +7 unit (StampFields),
+4 integration (DrawingStampTests: заполнение+reread 3 граф, перезапись, пустой
ввод, не-чертёж). Итого 123 unit + 65 integration = 188.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 17:20:16 +03:00

119 lines
8.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Дизайн: основная надпись чертежа (drawing_fill_title_block) через API7
**Дата:** 2026-05-27
**Статус:** реализовано и проверено (4 интеграционных + 7 unit, всего 188); ревью Codex спека учтено
## Находка спайка: `Str` ПЕРЕЗАПИСЫВАЕТ (Clear не нужен)
Двойная запись в графу 2 («ДЕТАЛЬ.001» → «ИЗДЕЛИЕ.002») → чтение даёт «ИЗДЕЛИЕ.002» (не
дописано). Значит `stamp.Text[id].Str = value` **замещает** содержимое — `IText.Clear()` перед
записью НЕ нужен.
## Правки по ревью Codex
- **Update() результат проверять**: `if (!stamp.Update()) throw`.
- **null-checks**: `LayoutSheets`, `sheet`, `sheet.Stamp` (ранняя диагностируемая ошибка).
- **`RequireActiveDrawing()`** сохраняет проверку `doc is not IKompasDocument2D` (не только
`DocumentType`); `RequireActiveDrawingViews` становится надстройкой над ним.
- **`ColumnId` switch** — дефолт `ArgumentOutOfRangeException`; unit-тест на `(StampField)999`.
- **Тест**: писать и перечитывать ВСЕ три графы (вкл. `name`=графа 1); явный reread `Text[id].Str`;
тест overwrite (запись дважды → читается второе) — уже подтверждён спайком, перенесём в регрессию.
- **`name` = «Наименование изделия» (графа 1, `ksStPartNumber`)** — однозначно в описании (не путать
с `ksStDocumentName=51`).
- **Счётчик результата** = число непустых (trimmed) полей, записанных до успешного `Update()`.
- **Сигнатура**: `FillTitleBlockAsync(string? designation = null, string? name = null,
string? material = null, ct)`.
- **Scale (графа 6) и прочие — ЯВНО вне scope инкремента 2.** Spike-метод и spike-тест удаляются.
- RCW точечно не освобождаем (v2-2).
## Цель
Веха 2D-ЧЕРТЁЖ, инкремент 2 (оформление). После стандартных видов (`drawing_create_standard_views`)
— заполнить **основную надпись** (штамп, title block) чертежа: обозначение, наименование, материал.
Чертёж с видами + заполненным штампом = презентабельный конструкторский документ. Размеры на видах
— отдельный инкремент 3 (нужен спайк по размерным интерфейсам).
## Спайк: механизм подтверждён вживую (НЕ разучивать)
Спайк (`DrawingStampSpikeTests`, зелёный) подтвердил путь API7 — round-trip графы:
```
IKompasDocument2D doc2d = (IKompasDocument2D)app.ActiveDocument; // активный чертёж
ILayoutSheet sheet = (ILayoutSheet)doc2d.LayoutSheets.ItemByNumber[1]; // лист 1 (1-based)
IStamp stamp = (IStamp)sheet.Stamp;
stamp.Text[columnId].Str = text; // Text[Int32 Id] — индексированное свойство; Id = номер графы
stamp.Update(); // фиксирует
string readBack = stamp.Text[columnId].Str; // чтение для проверки
```
**Проверено:** запись в графу 2 (обозначение) «ДЕТАЛЬ.001» → чтение возвращает «ДЕТАЛЬ.001».
`IText.Str` — read/write; `IStamp.Text[Id]` — индексированное свойство (рефлексия: `get_Text(Int32 Id)`).
`Update()` фиксирует; перестроение документа не нужно.
**Номера граф ГОСТ (форма 1), `ksStampEnum`:** `ksStPartNumber=1` (наименование изделия),
`ksStDescription=2` (обозначение документа), `ksStMaterial=3` (материал), `ksStScale=6` (масштаб),
`ksStAuthor=110` (разработал) и т.д.
## Архитектура
Метод в существующем `DrawingService`. Свой enum `StampField` (паттерн `BasePlane`/`MateType`)
маппится на номера граф — COM-числа не текут в Host/тесты.
- `src/Kompas.Mcp.Core/Drawings/StampField.cs` — enum `StampField` + static `StampFields`
(`ColumnId(field)` → номер графы).
- `DrawingService.FillTitleBlockAsync(designation?, name?, material?, ct)` — реализация.
- Инструмент `drawing_fill_title_block` в `DrawingTools`.
## MCP-инструмент
| Инструмент | Параметры | Поведение |
|---|---|---|
| `drawing_fill_title_block` | `designation?`, `name?`, `material?` (все опц.) | Заполнить графы основной надписи активного чертежа: обозначение (графа 2), наименование (1), материал (3). Передаются только нужные поля (null/пусто — пропускается). Возвращает число заполненных граф. |
Контракт: активный документ — чертёж; хотя бы одно поле задано (иначе ошибка). Набор полей —
3 ключевые графы ГОСТ (расширяемо). За один вызов заполняются все переданные (меньше round-trip'ов
для LLM-агента, чем у «одна графа за вызов»).
## Валидация (чистые static, unit-тест)
- `StampFields.ColumnId(StampField)` — `Designation→2`, `Name→1`, `Material→3` (unit-тест маппинга).
- В сервисе: собрать непустые (после `Trim`) поля; если ни одного — `ArgumentException`
«укажите хотя бы одно поле». (Чистый помощник `StampFields.Collect(...)` → список (columnId,text)
— unit-тестируемо без COM.)
## Проверка активного документа
`RequireActiveDrawing()` → `IKompasDocument2D` (тип `ksDocumentDrawing`, иначе раздельная ошибка) —
выделить из существующего `RequireActiveDrawingViews` общий шаг (тот станет надстройкой: вернуть
`.ViewsAndLayersManager.Views`).
## Реализация
`FillTitleBlockCore`: собрать поля (`StampFields.Collect`) → если пусто, ошибка → `RequireActiveDrawing`
→ `sheet = LayoutSheets.ItemByNumber[1]` → `stamp = (IStamp)sheet.Stamp` → для каждого поля
`stamp.Text[columnId].Str = text` → `stamp.Update()` (результат проверить) → вернуть число граф.
Транзитные RCW не освобождаем точечно (долг v2-2, как везде).
## Тестирование
### Unit (`StampFieldsTests`)
- `StampFields.ColumnId` — каждое поле → ожидаемый номер графы (2/1/3).
- `StampFields.Collect` — пропускает null/пусто/пробелы; при всех пустых даёт пустой список;
собирает заданные с обрезкой пробелов.
### Integration (`DrawingStampTests`)
Паттерн: создать чертёж → `FillTitleBlockAsync(...)` → перечитать графы напрямую (helper в сервисе
`ReadStampCellAsync(columnId)` для теста ИЛИ проверка через возврат) → `finally CloseAsync` (база
`IntegrationTestBase`).
1. **Заполнение и чтение**: `designation="ДЕТАЛЬ.001", material="Сталь 45"` → 2 графы; чтение графы
2 == «ДЕТАЛЬ.001», графы 3 == «Сталь 45».
2. **Пустой ввод**: все поля null → `ArgumentException` (ни одной графы не тронуто).
3. **Не-чертёж**: активный документ — деталь → ошибка.
(Для чтения в тесте — добавить `DrawingService.ReadStampCellAsync(columnId)` — он же подтверждает
round-trip; спайк-файл удаляется.)
## Дальнейшее (вне спека)
- Прочие графы (масштаб, разработал, даты, литера), размеры/обозначения на видах (инкремент 3),
рамка/формат листа.