#1: Valid==false → ошибка (откат), а не «успех с valid=false»; косвенно отсекает грани одного компонента (вырожденный мат). #6/#7: distance value строго > 0 (нулевой зазор = coincidence) + unit-кейс value=0 бросает. #8: негативный интеграционный тест — точка в пустоте → грань не найдена → ошибка. #2/#3/#4 оставлены по обоснованию (best-effort откол, Add??throw, первый IFace — консистентно с HoleService). #9 точечный release RCW отклонён (конвенция v2-2). Тесты: 98 unit + 58 integration = 156 зелёных. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
12 KiB
Дизайн: сопряжение компонентов сборки (assembly_add_mate) через API7
Дата: 2026-05-27 Статус: реализовано и проверено (2 интеграционных + 14 unit, всего 154); ревью Codex спека (8) и реализации (9) учтено
Правки по ревью реализации Codex (9 замечаний)
- #1
Valid==false→ ошибка (а не «успех с valid=false»): после Update+Rebuild проверяемmate.Valid, иначе бросаем (срабатывает откат).AddedMate.Validтеперь всегда true при успехе. Это же косвенно ловит грани одного компонента (#5) — мат выродится. - #6/#7 distance
value> 0 (не ≥0): нулевой зазор вырождается в coincidence →RequireMateValueтребует строго> 0; unit-кейсvalue=0бросает. - #8 негативный интеграционный тест: точка в пустоте → грань не найдена → ошибка.
- #2 откат best-effort оставлен намеренно (исходное исключение важнее ошибки отката; паттерн
HoleService). #3mates.Add ?? throwдо try корректно (null = ничего не добавлено). #4 «первый IFace» оставлен (консистентно с HoleService/move_face). #9 точечный release RCW отклонён (конвенция v2-2).
Решения по ревью Codex (важные правки к дизайну ниже)
- Scope урезан до
coincidence+distance(#6, #5): только эти два типа геометрически подтверждены спайком → только они в публичномMateType/Parse. Остальные (parallel/ perpendicular/concentric/angle/tangency) иAlignment-параметр — будущие инкременты с тестами. - Валидация value по типу (#1):
Mates.RequiresValue→ distance требуетvalueконечный и≥ 0; для типов без value параметр игнорируется (дефолт 0). ВалидаторAssemblyValidation.RequireMateValue(requiresValue, value). - Полный откат (#3): весь участок после
mates.Add— в try/catch; при ЛЮБОЙ ошибке (BaseObject, ParamValue, Update=FALSE, RebuildModel=FALSE) сопряжение удаляется черезmate.Owner?.Delete()(IFeature7, как у компонента в инкременте 1). - Возврат результата (#8b):
AddedMate { Type: string, Valid: bool }— для отчёта инструмента и ассертов теста (Valid). - Усиленный тест (#2): явно
bbBefore ≈ 120; после mate —Valid==true, размеры каждого компонента (SizeX≈20) неизменны, ширина сборки изменилась. - RCW (#7): точечный release НЕ вводим (конвенция v2-2, как в инкременте 1/HoleService); все
COM-вызовы — внутри
dispatcher.InvokeAsync(уже так). Долг зафиксирован в OPEN_QUESTIONS v2-2. - Выбор грани (#8a): паттерн «первый IFace» (как в HoleService/move_face); в описании инструмента — «точка в СЕРЕДИНЕ грани» (на ребре/углу хит неоднозначен).
Цель
Веха СБОРКИ, инкремент 2. После вставки компонентов (assembly_add_component, инкремент 1) —
сопряжения: наложить связь между гранями двух компонентов (совпадение / на расстоянии),
чтобы решатель сборки спозиционировал компоненты относительно друг друга. Это вторая
половина минимально полезного цикла сборки (вставить → связать).
Спайк: механизм подтверждён вживую (НЕ разучивать)
Спайк (AssemblyMateSpikeTests, оба теста зелёные) подтвердил полный путь API7:
IPart7 top = (IPart7)doc3d.TopPart; // активная сборка
IMateConstraints3D mates = top.MateConstraints; // COM-свойство (не QI)
IMateConstraint3D mate = mates.Add(MateConstraintType); // напр. mc_Coincidence=0 / mc_Distance=5
mate.BaseObject1 = (IModelObject)face1; // грань компонента 1
mate.BaseObject2 = (IModelObject)face2; // грань компонента 2
if (distance|angle) mate.ParamValue = value; // зазор (мм) / угол (°)
bool ok = mate.Update(); // наложить сопряжение
top.RebuildModel(true); // применить к геометрии
Выбор граней компонентов: top.FindObjectsByPoint(x, y, z, FirstLevel=false) — FirstLevel=false
заставляет спуститься В компоненты и вернуть их грань в контексте сборки (мировые координаты с
учётом placement компонента). Это именно тот IModelObject, который нужен решателю сопряжений.
Проверенные результаты (2 куба 20³, A в нуле x[0,20], B со сдвигом +100 x[100,120]):
- Совпадение (
mc_Coincidence) +X грани A (20,10,10) с −X гранью B (100,10,10):Update=True,Valid=True,Alignment=ksMCAlignmentClosest(дефолт). Грани сошлись заподлицо → ширина сборки по X 120 → 40 (кубы соприкасаются). Решатель подвинул A (к B), не B. - Расстояние (
mc_Distance,ParamValue=30) тех же граней:Update=True,Valid=True. Грани встали в 30 мм → ширина сборки по X 120 → 70 (20+30+20).
Тонкости (эмпирика):
AlignmentНЕ выставляем — дефолт работает (ksMCAlignmentClosestдля совпадения,ksMCAlignmentUnknownдля расстояния). Параметр выравнивания — расширение позже (YAGNI).- Решатель может двигать любой компонент (в т.ч. первый, «зафиксированный»). Поэтому результат проверяем по ширине габарита (грани сблизились), а не по тому, какой компонент сдвинулся.
mates.AddвозвращаетIMateConstraint3D; результатUpdate()проверяем (FALSE = не наложилось). После —RebuildModel(true)(результат проверяем, как в инкременте 1).
Архитектура
Метод в существующем AssemblyService (он уже про сборки). Свой enum MateType (паттерн
BasePlane/CoordinateAxis) маппится на КОМПАС MateConstraintType — COM-константы не текут в
Host/тесты.
src/Kompas.Mcp.Core/Assemblies/MateType.cs— enumMateType+ staticMates(ToConstraintType,Parse(string)англ./рус.,RequiresValue).AssemblyService.AddMateAsync(MateType, x1,y1,z1, x2,y2,z2, value, ct)— реализация.- Инструмент
assembly_add_mateвAssemblyTools(тип сопряжения строкой →Mates.Parse).
MCP-инструмент
| Инструмент | Параметры | Поведение |
|---|---|---|
assembly_add_mate |
mateType: string, x1,y1,z1, x2,y2,z2, value=0 |
Наложить сопряжение между гранями двух компонентов активной сборки, выбранными по мировым точкам (x1..z1) и (x2..z2). mateType: coincidence / distance / parallel / perpendicular / concentric / angle / tangency. Для distance — value зазор (мм), для angle — value угол (°). |
Контракт: активный документ — сборка (RequireActiveAssembly, уже есть); обе точки должны попасть
на грани компонентов. Гарантированно валидированы геометрией: coincidence, distance. Остальные
типы маппятся и вызываемы через тот же путь Add+BaseObject+Update, но не покрыты регрессионным
тестом по габариту (требуют иной геометрии/проверки) — помечены в описании инструмента.
Валидация (чистые static, unit-тест)
Mates:
Parse(string)— англ./рус. →MateType; иначеArgumentExceptionсо списком допустимых.ToConstraintType(MateType)— →MateConstraintType.RequiresValue(MateType)— true для Distance/Angle.
В сервисе: RequireFinitePosition для обеих точек (уже есть в AssemblyValidation); если
RequiresValue — проверить double.IsFinite(value) (новый чистый валидатор
AssemblyValidation.RequireFiniteValue).
Реализация
AddMateCore: валидация точек (и value, если нужен) → RequireActiveAssembly →
FindFaceInAssembly (FirstLevel=false) для обеих точек (понятная ошибка, если грань не найдена) →
mates.Add(Mates.ToConstraintType(type)) → BaseObject1/2 → ParamValue (если нужен) →
Update() (FALSE → откат сопряжения через IFeature7.Delete + ошибка) → RebuildModel(true)
(FALSE → ошибка). Транзитные RCW не освобождаем точечно (долг v2-2, как везде).
Тестирование
Unit (MateTests или в AssemblyValidationTests)
Mates.Parse— англ. и рус. синонимы → верныйMateType; бросает на мусоре.Mates.ToConstraintType— каждыйMateType→ ожидаемыйMateConstraintType.Mates.RequiresValue— true для Distance/Angle, false для прочих.AssemblyValidation.RequireFiniteValue— бросает на NaN/Infinity.
Integration (AssemblyMateTests)
Паттерн: построить куб 20³ → сохранить → создать сборку → вставить 2 компонента (A в нуле, B +100)
→ сопряжение → проверить ширину габарита → finally CloseAsync.
- Совпадение граней A(+X)/B(−X): ширина по X 120 → ~40 (соприкосновение).
- Расстояние 30: ширина по X 120 → ~70 (зазор 30). (Эти два сценария = промотированный спайк; спайк-файл удаляется.)
Дальнейшее (вне спека)
- Выравнивание (
Alignment), типы concentric/angle/parallel с геометрической проверкой. - Перемещение/фиксация/массив компонентов.