From a8f7363869209b3ff7d4c49735d7679617d5040d Mon Sep 17 00:00:00 2001 From: Shahovalov MIkhail Date: Wed, 27 May 2026 12:50:35 +0300 Subject: [PATCH] =?UTF-8?q?docs:=20=D1=81=D0=BF=D0=B5=D0=BA=20assembly=5Fa?= =?UTF-8?q?dd=5Fcomponent=20=E2=80=94=20=D0=B2=D1=81=D1=82=D0=B0=D0=B2?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=B4=D0=B5=D1=82=D0=B0=D0=BB=D0=B8=20=D0=B8?= =?UTF-8?q?=D0=B7=20.m3d=20=D0=B2=20=D1=81=D0=B1=D0=BE=D1=80=D0=BA=D1=83?= =?UTF-8?q?=20(API7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Веха СБОРКИ, инкремент 1. Новый AssemblyService (API7, паттерн HoleService): AddFromFile → Placement.SetOrigin → UpdatePlacement. Сигнатуры подтверждены рефлексией interop. Валидация — чистые static. Тест — габаритом/числом компонентов. Co-Authored-By: Claude Opus 4.7 (1M context) --- ...026-05-27-assembly-add-component-design.md | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 docs/superpowers/specs/2026-05-27-assembly-add-component-design.md diff --git a/docs/superpowers/specs/2026-05-27-assembly-add-component-design.md b/docs/superpowers/specs/2026-05-27-assembly-add-component-design.md new file mode 100644 index 0000000..321c4b4 --- /dev/null +++ b/docs/superpowers/specs/2026-05-27-assembly-add-component-design.md @@ -0,0 +1,116 @@ +# Дизайн: вставка компонента в сборку (assembly_add_component) через API7 + +**Дата:** 2026-05-27 +**Статус:** спроектировано (сигнатуры подтверждены рефлексией interop + kompas-sdk-research); на ревью (Codex) + +## Цель + +Открыть новый класс функционала — **СБОРКИ**. До сих пор сделана только геометрия ДЕТАЛИ. +Первый инкремент вехи: **вставить деталь из сохранённого `.m3d`-файла в активную сборку** с +заданием положения. Это фундамент — без вставки компонентов сборка пуста. + +`list_components` (чтение сборки) уже есть; теперь учимся её НАПОЛНЯТЬ. + +## Архитектурное решение + +Сборки — это **НЕ `PartModeler`** (он строит API5-деталь `ksPart`). Заводим **новый сервис +`AssemblyService`** (API7, паттерн как у `HoleService`/`FaceEditService`: контейнеры берём +COM-QI, операции на STA-потоке `KompasDispatcher`) и **новый класс инструментов `AssemblyTools`**. + +- `src/Kompas.Mcp.Core/Assemblies/AssemblyService.cs` — сервис. +- `src/Kompas.Mcp.Core/Assemblies/AssemblyValidation.cs` — чистые static-валидаторы (unit-тест). +- `src/Kompas.Mcp.Core/Assemblies/AddedComponent.cs` — record результата (Index, Name). +- `src/Kompas.Mcp.Host/Tools/AssemblyTools.cs` — MCP-инструмент. +- DI: `AddSingleton()` в `Program.cs`. + +(Namespace `Kompas.Mcp.Core.Assemblies` — во множественном числе, чтобы не конфликтовать с +`System.Reflection.Assembly`.) + +## Подтверждённый workflow (рефлексия interop + справка) + +Рефлексией `KompasAPI7.dll` подтверждено: +- `IParts7.AddFromFile(string FileName, bool ExternalFile, bool Redraw)` → `Part7`. +- `IPart7.Placement` (`Placement3D`), `IPart7.UpdatePlacement(bool Redraw)`. +- `IPlacement3D.SetOrigin(double X, double Y, double Z)`. + +``` +IKompasDocument3D doc3d = (IKompasDocument3D)app.ActiveDocument; // должна быть СБОРКА +// валидация: ((IKompasDocument)doc3d).DocumentType == ksDocumentAssembly +IPart7 top = (IPart7)doc3d.TopPart; +IParts7 parts = top.Parts; // COM-свойство + +IPart7 part = (IPart7)parts.AddFromFile(filePath, /*ExternalFile*/ true, /*Redraw*/ true); +// ExternalFile=true: компонент — ссылка на внешний .m3d (что нам и нужно — отдельный файл-источник). +// Redraw=true: перестроить сразу (отдельный RebuildModel не нужен). + +IPlacement3D pl = (IPlacement3D)part.Placement; // read-only свойство, мутируем +pl.SetOrigin(x, y, z); // мировые координаты сборки, мм +part.UpdatePlacement(/*Redraw*/ true); // зафиксировать перемещение +``` + +**Почему `AddFromFile`, а не `AddFromFileWithParam`:** для первого инкремента нужен только сдвиг +origin. `AddFromFile` + `Placement.SetOrigin` + `UpdatePlacement` — минимальный надёжный путь +(подтверждён рефлексией: все три члена есть в interop). `AddFromFileWithParam` требует получить +`IPlacement3D`/`IInsertPartParameters` через `IKompasDocument1.GetInterface(код)` до вставки — это +лишняя неочевидность (коды 11223/13196 не проверены рефлексией) и YAGNI на этом шаге. Ориентацию +(`SetVector`/углы Эйлера) и параметры вставки добавим в следующих инкрементах при необходимости. + +**Единицы:** `x/y/z` — миллиметры в мировой СК сборки. По умолчанию (0,0,0) компонент встаёт в +начало координат сборки. + +**Фиксация:** через API первый компонент НЕ фиксируется автоматически. На этом шаге фиксацию НЕ +выставляем (YAGNI) — для теста объёмом/габаритом она не нужна; добавим параметр `fixed` позже, +если понадобится. + +## MCP-инструмент (новая группа Assembly) + +| Инструмент | Параметры | Поведение | +|---|---|---| +| `assembly_add_component` | `filePath: string`, `x=0`, `y=0`, `z=0` | Вставить деталь/подсборку из `.m3d`/`.a3d`-файла в активную сборку; origin компонента — (x,y,z) мм. Возвращает индекс и имя вставленного компонента. | + +Контракт: активный документ должен быть **сборкой** (иначе понятная ошибка); файл должен +существовать. Возврат — текст вида «Компонент '<имя>' вставлен в сборку, индекс N, позиция (x,y,z)». + +## Валидация (чистые static, unit-тест) + +`AssemblyValidation`: +- `RequireComponentPath(string? path)` — путь непустой (формат строки). +- `RequireFinitePosition(double x, double y, double z)` — координаты конечны (`double.IsFinite`; + отсекает `Infinity`/`NaN`, как в `HoleService`). + +Проверка существования файла (`File.Exists`) — в сервисе (не чистая, не unit-тест), даёт +`FileNotFoundException` с путём. + +## Реализация + +- `AssemblyService.AddComponentAsync(string filePath, double x, double y, double z, ct)` — + на STA-потоке: валидация → проверка типа активного документа (сборка) → `AddFromFile` → + `Placement.SetOrigin` → `UpdatePlacement` → вернуть `AddedComponent(Index, Name)`. + Индекс = `parts.Count - 1` после вставки; имя = `part.Name`. +- Транзитные RCW не освобождаем точечно — консистентно с остальным слоем (долг v2-2). + +## Тестирование + +### Unit (`AssemblyValidationTests`) +- `RequireComponentPath` бросает на null/пустой/пробельный; пропускает валидный. +- `RequireFinitePosition` бросает на NaN/Infinity по каждой координате; пропускает конечные. + +### Integration (`AssemblyTests`, Collection KompasCollection) +Паттерн: построить деталь → `SaveAsAsync(.scratch/*.m3d)` → `CloseAsync` → создать сборку → +`AddComponentAsync` → проверить → `finally CloseAsync`. + +1. **Вставка в начало координат:** деталь-коробка 40×40×20 (эскиз 0..40 × 0..40, выдавить 20), + сохранить, закрыть. Создать сборку, `AddComponentAsync(file, 0,0,0)`. + - `list_components` → `Count == 1`; компонент: `SizeX≈40, SizeY≈40, SizeZ≈20` (±допуск). + - `get_bounding_box` сборки: X∈[0,40], Y∈[0,40], Z∈[0,20] (±допуск). +2. **Позиционирование:** та же деталь, `AddComponentAsync(file, 50, 0, 0)`. + - `get_bounding_box` сборки: X∈[50,90] (сдвиг origin подтверждает работу позиции). + +Объёмом сборку тоже можно проверить (`get_part_info` на TopPart агрегирует компоненты) — +но габарит надёжнее и прямо проверяет позицию. Артефакты `.m3d` — в gitignored `.scratch/`. + +## Дальнейшие инкременты (вне этого спека) +2. `assembly_add_mate` — сопряжения (`IMateConstraints3D.Add(mc_Coincidence/mc_Distance/…)` + + `BaseObject1/2` + `Update`). Типы: совпадение/параллельность/перпендикулярность/касательность/ + концентричность/расстояние/угол/симметрия. +3. Перемещение/массив/фиксация компонентов — по необходимости.