docs: спек assembly_add_component — вставка детали из .m3d в сборку (API7)
Веха СБОРКИ, инкремент 1. Новый AssemblyService (API7, паттерн HoleService): AddFromFile → Placement.SetOrigin → UpdatePlacement. Сигнатуры подтверждены рефлексией interop. Валидация — чистые static. Тест — габаритом/числом компонентов. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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<AssemblyService>()` в `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. Перемещение/массив/фиксация компонентов — по необходимости.
|
||||
Reference in New Issue
Block a user