5272066bef
drawing_add_rough (знак шероховатости: ISymbols2DContainer.Roughs + IRoughParams, SignType ksRoughSignEnum, значение Ra/Rz через RoughParamText.Str), drawing_add_text (свободная надпись: IDrawingContainer.DrawingTexts + IText.Str), drawing_set_technical_requirements (блок тех. требований уровня документа: IDrawingDocument.TechnicalDemand.Text). - DrawingService: AddRoughAsync/AddTextAsync/SetTechnicalRequirementsAsync + ReadTechnicalRequirementsAsync + счётчики; RequireDrawingContainer/RequireTechnicalDemand - RoughSignType (enum + Parse/ToKompas), DrawingAnnotationResult (Value read-back из COM) - RequireNonEmptyText в DrawingValidation - 13 unit + 13 интеграционных тестов (всего 267 зелёных), сборка Release чистая Спайк подтвердил: значение шероховатости через IRoughParams.RoughParamText.Str (round-trip); текст НЕ в IView.ObjectCount (счёт по DrawingTexts.Count); тех. требования IsCreated False→True при первом Text.Str+Update, многострочно через \n. Ревью Codex спека учтено (height убран — это высота блока, не шрифт; guards; read-back возврат). Спек — docs/superpowers/specs/. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
168 lines
14 KiB
Markdown
168 lines
14 KiB
Markdown
# Дизайн: текстовые обозначения чертежа — шероховатость, текст, тех. требования (API7)
|
||
|
||
**Дата:** 2026-05-27
|
||
**Статус:** дизайн согласован (все три в одном инкременте), спайк проведён, ревью Codex спека учтено — к реализации
|
||
|
||
## Правки по ревью Codex (спек)
|
||
|
||
- **#4** параметр `height` у `drawing_add_text` **убран:** `IDrawingText.Height` — высота блока
|
||
форматирования, НЕ размер шрифта (шрифт задаётся на уровне `ITextItem`, вне объёма). Текст ставится
|
||
стилем по умолчанию. (Снимает и #10 — валидация height не нужна.)
|
||
- **#3/#11** сервис возвращает значение, **прочитанное обратно из COM** после `Update` (rough —
|
||
`RoughParamText.Str`; text — `((IText)dt).Str`) — как `NominalValue` у размеров. Тест `Value==вход`
|
||
доказывает round-trip. Пост-Add откат `Delete` есть в коде; не форсируется тестом (для валидных
|
||
параметров COM всегда `Valid` — как решено у диаметрального; путь идентичен протестированному угловому).
|
||
- **#1** `value` шероховатости нормализуется (`null`/whitespace → `""`); возвращается нормализованное
|
||
(read-back), контракт `Value` непустой не нарушается.
|
||
- **#2/#5/#7** добавлены null/QI-guards: `Roughs`/`(IRoughParams)`/`RoughParamText`;
|
||
`(IDrawingContainer)`/`DrawingTexts`/`(IText)`; `(IDrawingDocument)`/`TechnicalDemand`/`Text` —
|
||
каждый с раздельной диагностикой (как `RequireStampCell`/`RequireSymbols2DContainer`).
|
||
- **#6** тесты текста дополнены: многострочный round-trip (`\n`), несуществующий `viewNumber>0`.
|
||
- **#8** добавлен helper `ReadTechnicalRequirementsAsync` — тест читает `td.Text.Str` и проверяет
|
||
перезапись (как `DrawingStampTests` перечитывают графы).
|
||
- **#9** подсчёт строк тех. требований нормализован: `text.Replace("\r\n","\n")`, хвостовые пустые
|
||
строки отбрасываются (`"a\n"` → 1 строка).
|
||
|
||
## Цель
|
||
|
||
Веха 2D-ЧЕРТЁЖ, инкремент 6. После завершения базового семейства размеров — **текстовые обозначения**:
|
||
знак шероховатости, свободная текстовая надпись, технические требования. Три инструмента покрывают
|
||
самые частые «не-размерные» элементы конструкторского чертежа.
|
||
|
||
## Спайк: все три механизма подтверждены вживую (НЕ разучивать)
|
||
|
||
Спайк (`_SpikeAnnotations`, прогнан на реальном КОМПАС v24, затем удалён) на коробке 40×30×20 с
|
||
тремя стандартными видами.
|
||
|
||
### Шероховатость — на виде (`ISymbols2DContainer.Roughs`)
|
||
|
||
```
|
||
IRough rough = symbols.Roughs.Add(); // без параметров
|
||
rough.BranchX0 = 20; rough.BranchY0 = 25; // положение знака (ЛОКАЛЬНАЯ СК вида, мм)
|
||
rough.Angle = 0; // угол наклона оси знака (градусы)
|
||
IRoughParams rp = (IRoughParams)rough; // QI (как IDimensionText у размеров)
|
||
rp.SignType = ksRoughSignEnum.ksDeleteMaterial; // тип знака
|
||
rp.RoughParamText.Str = "Ra 1.6"; // значение (Ra/Rz) — текст
|
||
rough.Update(); // True, rough.Valid == True
|
||
```
|
||
|
||
**Проверено:** `Update=True`, `Valid=True`, `RoughParamText.Str` round-trip = `"Ra 1.6"`, `Roughs.Count`
|
||
→ 1. `ksRoughSignEnum`: `ksNoProcessingType=0` (без указания обработки), `ksDeleteMaterial=1` (с
|
||
удалением слоя материала), `ksWithoutDeleteMaterial=2` (без удаления). Положение — свободные координаты
|
||
(`BranchX0/Y0`); `BaseObject` (привязка к контуру) не задаём — будущее.
|
||
|
||
### Свободный текст — на виде (`IDrawingContainer.DrawingTexts`, НЕ Symbols!)
|
||
|
||
```
|
||
IDrawingContainer dc = (IDrawingContainer)view; // ВНИМАНИЕ: текст в контейнере геометрии,
|
||
IDrawingText dt = dc.DrawingTexts.Add(); // а НЕ в ISymbols2DContainer
|
||
dt.X = 30; dt.Y = 45; dt.Angle = 0; // точка привязки (ЛОКАЛЬНАЯ СК вида, мм)
|
||
((IText)dt).Str = "Образец надписи"; // содержимое (QI к IText), \n — многострочно
|
||
dt.Update(); // True, dt.Valid == True
|
||
```
|
||
|
||
**Проверено:** `Update=True`, `Valid=True`, `Str` round-trip, `DrawingTexts.Count` → **2** (вид уже
|
||
содержал 1 текст — авто-подпись вида; проверять по ДЕЛЬТЕ before+1, не по абсолюту). `ObjectCount`
|
||
4→4 — текст **НЕ** входит в `IView.ObjectCount` (как и размеры) → проверять `DrawingTexts.Count`.
|
||
|
||
### Технические требования — на уровне ДОКУМЕНТА (`IDrawingDocument.TechnicalDemand`)
|
||
|
||
```
|
||
IDrawingDocument dd = (IDrawingDocument)doc; // QI от активного IKompasDocument2D
|
||
ITechnicalDemand td = dd.TechnicalDemand; // единый блок на документ
|
||
td.Text.Str = "1. Общие допуски по ГОСТ 30893.1.\n2. Острые кромки притупить."; // \n — строки
|
||
td.Update(); // True
|
||
```
|
||
|
||
**Проверено:** `IsCreated` False→True (первый `Text.Str`+`Update()` создаёт блок), `Update=True`, текст
|
||
с `\n` сохранён построчно. Объект уровня документа (не вида), единственный, над основной надписью.
|
||
`Str` замещает содержимое (как у штампа) → инструмент `set` (перезапись).
|
||
|
||
## MCP-инструменты
|
||
|
||
| Инструмент | Параметры | Поведение |
|
||
|---|---|---|
|
||
| `drawing_add_rough` | `x,y` (положение знака), `value=""` (Ra/Rz, напр. "Ra 1.6"), `signType="delete"` (delete\|without\|none), `angle=0` (°), `viewNumber=0` | Поставить знак шероховатости на виде. Положение `x,y` в ЛСК вида (мм). `value` — текст параметра (пусто = знак без значения). Возвращает значение и номер вида. |
|
||
| `drawing_add_text` | `x,y` (точка привязки), `text`, `angle=0` (°), `viewNumber=0` | Поставить свободную текстовую надпись на виде стилем по умолчанию. `text` — содержимое (`\n` — многострочно). Возвращает текст (read-back) и номер вида. |
|
||
| `drawing_set_technical_requirements` | `text` | Задать технические требования активного чертежа (единый блок над штампом; перезаписывает прежние). `text` — строки через `\n`. Возвращает число строк. |
|
||
|
||
## Архитектура
|
||
|
||
В существующем `DrawingService` (namespace `Kompas.Mcp.Core.Drawings`). Шероховатость/текст —
|
||
per-view; тех. требования — per-document.
|
||
|
||
- `AddRoughAsync(viewNumber, x, y, value, signType, angleDeg, ct)` → `DrawingAnnotationResult`.
|
||
Введём `record DrawingAnnotationResult { string Value; int ViewNumber }` (универсальный для rough/text —
|
||
`Value`=строка, прочитанная обратно из COM; `ViewNumber`). Тех. требования возвращают число строк (int).
|
||
- `AddTextAsync(viewNumber, x, y, text, angleDeg, ct)` → `DrawingAnnotationResult`.
|
||
- `SetTechnicalRequirementsAsync(text, ct)` → `int` (число строк).
|
||
- `ReadTechnicalRequirementsAsync(ct)` → `string` (для теста — `td.Text.Str`).
|
||
- Helpers: `RequireSymbols2DContainer` (есть, для rough), новый `RequireDrawingContainer(viewNumber)`
|
||
= guarded `(IDrawingContainer)FindView(...)` (для text), новый `RequireDrawingDocument()` =
|
||
guarded `(IDrawingDocument)RequireActiveDrawing()` (для тех. требований). Все QI с раздельной диагностикой.
|
||
- Счётчики для тестов: `GetViewRoughCountAsync`, `GetViewTextCountAsync`.
|
||
- Новый enum-файл `RoughSignType.cs`: `enum RoughSignType {NoProcessing, DeleteMaterial, WithoutDeleteMaterial}`
|
||
+ `RoughSignTypes.Parse(string)`/`ToKompas(...)` (→ `ksRoughSignEnum`), по образцу `AngleDimensionTypes`.
|
||
- Новый файл `DrawingAnnotationResult.cs`.
|
||
- Инструменты в `DrawingTools.cs`.
|
||
|
||
## Валидация (чистые static, unit-тест)
|
||
|
||
- `RequireFiniteCoords` (есть) — координаты rough/text.
|
||
- Новый `RequireNonEmptyText(string, paramName)` — для `text` (drawing_add_text, тех. требования).
|
||
`value` шероховатости НЕ обязателен (знак без значения допустим; `null`/whitespace → `""`).
|
||
- `RoughSignTypes.Parse` — разбор строки (unit-тест; `ToKompas` → `ksRoughSignEnum` НЕ покрываем unit —
|
||
граница проекта, как у `AngleDimensionTypes`/`DimensionOrientations`).
|
||
|
||
## Реализация
|
||
|
||
**`AddRoughCore`**: нормализация `value` (`null`/whitespace → `""`) → `RequireFiniteCoords(x,y,angleDeg)`
|
||
→ `RequireSymbols2DContainer` → `symbols.Roughs` (null-check) → `.Add()` (null-check) → `BranchX0/Y0/Angle`
|
||
→ `(IRoughParams)rough` (null-check QI) → `SignType=ToKompas`, `rp.RoughParamText` (null-check) `.Str = value`
|
||
→ `Update()` (FALSE → откат `Delete`) → `Valid` (false → откат) → читаем `rp.RoughParamText.Str` обратно
|
||
→ `DrawingAnnotationResult{Value=read-back, ViewNumber}`.
|
||
|
||
**`AddTextCore`**: `RequireNonEmptyText(text)` + `RequireFiniteCoords(x,y,angleDeg)` →
|
||
`RequireDrawingContainer` → `dc.DrawingTexts` (null-check) → `.Add()` (null-check) → `X/Y/Angle` →
|
||
`(IText)dt` (null-check QI) `.Str = text` → `Update()` (FALSE → откат `Delete`) → `Valid` (false → откат)
|
||
→ читаем `((IText)dt).Str` обратно → `DrawingAnnotationResult{Value=read-back, ViewNumber}`.
|
||
|
||
**`SetTechnicalRequirementsCore`**: `RequireNonEmptyText(text)` → нормализация `text.Replace("\r\n","\n")`
|
||
→ `RequireDrawingDocument()` → `dd.TechnicalDemand` (null-check) → `td.Text` (null-check) `.Str = text` →
|
||
`td.Update()` (FALSE → ошибка; объект уровня документа — отката `Delete` НЕ делаем, перезапись идемпотентна)
|
||
→ вернуть число непустых-после-trim хвоста строк (`TrimEnd('\n').Split('\n').Length`).
|
||
|
||
**`RequireDrawingContainer(viewNumber)`** = `FindView` → `(IDrawingContainer)view` (null → «вид не приводится
|
||
к IDrawingContainer»). **`RequireDrawingDocument()`** = `RequireActiveDrawing()` → `(IDrawingDocument)doc`
|
||
(null → «чертёж не приводится к IDrawingDocument»).
|
||
|
||
RCW точечно не освобождаем (консистентно с остальным `DrawingService`; долг v2-2).
|
||
|
||
## Тестирование
|
||
|
||
### Unit (`RoughSignTypesTests`, `DrawingValidationTests`)
|
||
- `RoughSignTypes.Parse`: `delete/without/none` (+ рус. синонимы) → enum; неизвестное → `ArgumentException`; null → `ArgumentNullException`.
|
||
- `RequireNonEmptyText`: бросает на null/пусто/пробелы; пропускает непустое.
|
||
|
||
### Integration (`DrawingRoughTests`, `DrawingTextTests`, `DrawingTechReqTests`; наследуют `IntegrationTestBase`)
|
||
Шероховатость:
|
||
1. **Ставится + round-trip**: `value="Ra 1.6"`, `signType=delete` → `Value=="Ra 1.6"` (read-back из COM),
|
||
попадание в целевой вид (`Roughs.Count` +1, адресация по `viewNumber`).
|
||
2. **Знак без значения**: `value=""` → ставится (Valid), `Roughs.Count` +1.
|
||
3. **Нет видов** → понятная ошибка. 4. **Несуществующий `viewNumber>0`** → ошибка.
|
||
|
||
Текст:
|
||
5. **Ставится + round-trip**: `text` → `Value==text` (read-back), `DrawingTexts.Count` +1 (по ДЕЛЬТЕ —
|
||
вид уже мог содержать подпись), адресация по целевому `viewNumber`.
|
||
6. **Многострочный** (`"строка1\nстрока2"`) → `Value` сохраняет обе строки (round-trip `\n`).
|
||
7. **Пустой текст** → `ArgumentException` (до Add). 8. **Нет видов** → ошибка. 9. **Несуществующий `viewNumber>0`** → ошибка.
|
||
|
||
Тех. требования:
|
||
10. **Задаются + round-trip**: многострочный текст → `lines==2`; `ReadTechnicalRequirementsAsync` возвращает
|
||
тот же текст; повторный вызов с другим текстом перезаписывает (read-back = новый, без накопления).
|
||
11. **Пустой текст** → `ArgumentException`. 12. **Активный документ не чертёж** → понятная ошибка.
|
||
|
||
## Дальнейшее (вне спека)
|
||
- Привязка шероховатости/выносок к геометрии (`BaseObject`); выноски (`Leaders`), обозначения баз
|
||
(`Bases`), допуски формы (`Tolerances`); рамка/формат листа; ассоциативная привязка размеров.
|