From aec2f1e52f976a3634e1957837b0ca21df0e32c0 Mon Sep 17 00:00:00 2001 From: Shahovalov MIkhail Date: Wed, 27 May 2026 11:59:15 +0300 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=D1=82=D0=B8=D0=BF=D1=8B=20=D0=BE?= =?UTF-8?q?=D1=82=D0=B2=D0=B5=D1=80=D1=81=D1=82=D0=B8=D0=B9=20=E2=80=94=20?= =?UTF-8?q?=D1=86=D0=B5=D0=BA=D0=BE=D0=B2=D0=BA=D0=B0=20=D0=B8=20=D0=B7?= =?UTF-8?q?=D0=B5=D0=BD=D0=BA=D0=BE=D0=B2=D0=BA=D0=B0=20(hole=5Fcounterbor?= =?UTF-8?q?e,=20hole=5Fcountersink)=20=D1=87=D0=B5=D1=80=D0=B5=D0=B7=20API?= =?UTF-8?q?7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - HoleCore отрефакторен: параметр holeType + Action configure (настройка HoleParameters) - CounterboreHoleAsync (ksHTCounterbore + ISpotfacingHoleParameters: SpotfacingDiameter/Depth) - CountersinkHoleAsync (ksHTCountersinking + ICountersinkHoleParameters: ksCTDiameterAngle, Diameter/Angle) - переиспользуется размещение/подбор направления/откат из базового hole - инструменты hole_counterbore, hole_countersink; интеграционные тесты (объём ±10% / больше базы) - 69 инструментов, 118 тестов; спек docs/superpowers/specs/2026-05-27-hole-types-design.md --- .../specs/2026-05-27-hole-types-design.md | 51 +++++++++++++++++++ src/Kompas.Mcp.Core/Modeling/HoleService.cs | 47 +++++++++++++++-- src/Kompas.Mcp.Host/Tools/FeatureTools.cs | 32 ++++++++++++ .../Kompas.Mcp.Tests/Integration/HoleTests.cs | 51 +++++++++++++++++++ 4 files changed, 178 insertions(+), 3 deletions(-) create mode 100644 docs/superpowers/specs/2026-05-27-hole-types-design.md diff --git a/docs/superpowers/specs/2026-05-27-hole-types-design.md b/docs/superpowers/specs/2026-05-27-hole-types-design.md new file mode 100644 index 0000000..3a92069 --- /dev/null +++ b/docs/superpowers/specs/2026-05-27-hole-types-design.md @@ -0,0 +1,51 @@ +# Дизайн: типы отверстий — цековка и зенковка (расширение hole) + +**Дата:** 2026-05-27 +**Статус:** реализовано и проверено (тесты зелёные); на ревью (ревьюер — Codex) + +## Цель + +Расширить `hole` (простое цилиндрическое, `ksHTBase`) двумя ходовыми типами под крепёж: +**цековка** (`hole_counterbore`, под винт с цилиндрической головкой) и **зенковка** +(`hole_countersink`, под винт с потайной головкой). Через API7 (как базовое отверстие). + +## Сигнатуры (рефлексия interop) + +``` +IHole3D.HoleType = ksHoleTypeEnum: ksHTBase=0, ksHTCounterbore=1, ksHTCountersinking=2, ksHTConic=4 +IHole3D.HoleParameters : IKompasAPIObject // приводится к типу по HoleType (после установки HoleType) +ISpotfacingHoleParameters (цековка): SpotfacingDiameter, SpotfacingDepth (мм) +ICountersinkHoleParameters (зенковка): CountersinkType (ksCountersinkTypeEnum), CountersinkDiameter, CountersinkAngle (°), CountersinkDepth + ksCountersinkTypeEnum: ksCTDiameterAngle=0, ksCTDepthAngle=1, ksCTDiameterDepth=2 +``` + +## MCP-инструменты (группа Feature) + +| Инструмент | Параметры | Поведение | +|---|---|---| +| `hole_counterbore` | `x,y,z`, `diameter`, `spotfaceDiameter`, `spotfaceDepth`, `depth=0`, `throughAll=false` | Отверстие Ø`diameter` + цилиндрическая цековка Ø`spotfaceDiameter`×`spotfaceDepth` сверху. | +| `hole_countersink` | `x,y,z`, `diameter`, `sinkDiameter`, `sinkAngle`, `depth=0`, `throughAll=false` | Отверстие Ø`diameter` + коническая зенковка Ø`sinkDiameter` под углом `sinkAngle`° (тип `ksCTDiameterAngle`). | + +**Решения:** конический тип (`ksHTConic`) и резьба (`ShowThread`/`IThread`) — позже. Зенковка задаётся +парой диаметр+угол (`ksCTDiameterAngle`) — самый привычный ввод. + +## Реализация + +- **`HoleCore` отрефакторен**: добавлены параметры `ksHoleTypeEnum holeType` и `Action? configure` + (конфигуратор `HoleParameters` после установки `HoleType`). `HoleAsync` → `ksHTBase, null`. + Вся остальная логика (размещение через `IHoleDisposal`+`Points3D`, подбор направления по убыли + объёма, откат «сирот») переиспользуется. +- **`CounterboreHoleAsync`**: валидация (`spotDiameter>diameter`, `spotDepth>0`, finite) → + `HoleCore(..., ksHTCounterbore, hole => set ISpotfacingHoleParameters)`. +- **`CountersinkHoleAsync`**: валидация (`sinkDiameter>diameter`, `sinkAngle∈(0;180)`, finite) → + `HoleCore(..., ksHTCountersinking, hole => set ICountersinkHoleParameters: type=ksCTDiameterAngle)`. +- **Инструменты** `hole_counterbore`, `hole_countersink` в `FeatureTools.cs`. + +## Тестирование (Integration, `HoleTests`) + +- **Цековка**: коробка 40×40×20 → `hole_counterbore(0,0,20, Ø10, spot Ø20×5, throughAll)` → + удалено = база (π·25·20≈1571) + кольцо цековки (π·75·5≈1178) ≈ 2749 (`±10%`); и + `removed > базовый цилиндр·1.1` (уширение реально вырезано). ✓ +- **Зенковка**: коробка → `hole_countersink(0,0,20, Ø8, sink Ø16, 90°, throughAll)` → + `removed > база Ø8 (π·16·20≈1005)` и `< база·2` (конус добавил материал, в разумных пределах; + точный объём зависит от трактовки угла). ✓ diff --git a/src/Kompas.Mcp.Core/Modeling/HoleService.cs b/src/Kompas.Mcp.Core/Modeling/HoleService.cs index c8fc9a9..ecfa7ce 100644 --- a/src/Kompas.Mcp.Core/Modeling/HoleService.cs +++ b/src/Kompas.Mcp.Core/Modeling/HoleService.cs @@ -28,9 +28,49 @@ public sealed class HoleService /// diameter — диаметр (мм). throughAll — сквозное (depth игнорируется), иначе на глубину depth (мм). /// public Task HoleAsync(double x, double y, double z, double diameter, double depth, bool throughAll, CancellationToken ct = default) - => _dispatcher.InvokeAsync(() => HoleCore(x, y, z, diameter, depth, throughAll), ct); + => _dispatcher.InvokeAsync(() => HoleCore(x, y, z, diameter, depth, throughAll, ksHoleTypeEnum.ksHTBase, null), ct); - private void HoleCore(double x, double y, double z, double diameter, double depth, bool throughAll) + /// Отверстие с цековкой (цилиндрическим уширением сверху, под винт с цилиндрической + /// головкой): основное отверстие diameter + цековка spotDiameter×spotDepth. + public Task CounterboreHoleAsync(double x, double y, double z, double diameter, double depth, bool throughAll, + double spotDiameter, double spotDepth, CancellationToken ct = default) + => _dispatcher.InvokeAsync(() => + { + if (!double.IsFinite(spotDiameter) || spotDiameter <= diameter) + throw new ArgumentOutOfRangeException(nameof(spotDiameter), "Диаметр цековки должен быть больше диаметра отверстия."); + if (!double.IsFinite(spotDepth) || spotDepth <= 0) + throw new ArgumentOutOfRangeException(nameof(spotDepth), "Глубина цековки должна быть > 0."); + HoleCore(x, y, z, diameter, depth, throughAll, ksHoleTypeEnum.ksHTCounterbore, hole => + { + var p = hole.HoleParameters as ISpotfacingHoleParameters + ?? throw new InvalidOperationException("HoleParameters не приводится к ISpotfacingHoleParameters."); + p.SpotfacingDiameter = spotDiameter; + p.SpotfacingDepth = spotDepth; + }); + }, ct); + + /// Отверстие с зенковкой (коническим уширением сверху, под винт с потайной головкой): + /// основное отверстие diameter + зенковка sinkDiameter под углом sinkAngle (градусы, конус). + public Task CountersinkHoleAsync(double x, double y, double z, double diameter, double depth, bool throughAll, + double sinkDiameter, double sinkAngle, CancellationToken ct = default) + => _dispatcher.InvokeAsync(() => + { + if (!double.IsFinite(sinkDiameter) || sinkDiameter <= diameter) + throw new ArgumentOutOfRangeException(nameof(sinkDiameter), "Диаметр зенковки должен быть больше диаметра отверстия."); + if (!double.IsFinite(sinkAngle) || sinkAngle <= 0 || sinkAngle >= 180) + throw new ArgumentOutOfRangeException(nameof(sinkAngle), "Угол зенковки должен быть в (0; 180) градусов."); + HoleCore(x, y, z, diameter, depth, throughAll, ksHoleTypeEnum.ksHTCountersinking, hole => + { + var p = hole.HoleParameters as ICountersinkHoleParameters + ?? throw new InvalidOperationException("HoleParameters не приводится к ICountersinkHoleParameters."); + p.CountersinkType = ksCountersinkTypeEnum.ksCTDiameterAngle; // задаём диаметр + угол + p.CountersinkDiameter = sinkDiameter; + p.CountersinkAngle = sinkAngle; + }); + }, ct); + + private void HoleCore(double x, double y, double z, double diameter, double depth, bool throughAll, + ksHoleTypeEnum holeType, Action? configure) { if (!double.IsFinite(x) || !double.IsFinite(y) || !double.IsFinite(z)) throw new ArgumentException("Координаты точки отверстия должны быть конечными числами."); @@ -68,13 +108,14 @@ public sealed class HoleService { hole = container.Holes3D.Add() ?? throw new InvalidOperationException("Holes3D.Add() вернул null."); - hole.HoleType = ksHoleTypeEnum.ksHTBase; + hole.HoleType = holeType; hole.Diameter = diameter; hole.DepthType = throughAll ? ksDepthTypeEnum.ksDTReachThrough : ksDepthTypeEnum.ksDTValue; if (!throughAll) hole.Depth = depth; hole.EndFaceType = ksEndFaceTypeEnum.ksEFFlat; hole.Axis = false; hole.ShowThread = false; + configure?.Invoke(hole); // параметры цековки/зенковки (после установки HoleType) var disp = (IHoleDisposal)hole; disp.BaseSurface = (IModelObject)face; diff --git a/src/Kompas.Mcp.Host/Tools/FeatureTools.cs b/src/Kompas.Mcp.Host/Tools/FeatureTools.cs index e280357..f045718 100644 --- a/src/Kompas.Mcp.Host/Tools/FeatureTools.cs +++ b/src/Kompas.Mcp.Host/Tools/FeatureTools.cs @@ -212,6 +212,38 @@ public sealed class FeatureTools(KompasSession session, PartModeler modeler, Hol return $"Уклон {angle}° создан на {count} гранях (нейтральная плоскость {neutralPlane}), id={id}."; } + [McpServerTool(Name = "hole_counterbore")] + [Description("Отверстие с ЦЕКОВКОЙ (цилиндрическое уширение сверху — под винт с цилиндрической головкой) на грани в точке (x,y,z — центр). diameter — диаметр отверстия (мм). depth/throughAll — как у hole. spotfaceDiameter — диаметр цековки (мм, больше diameter), spotfaceDepth — глубина цековки (мм). Направление в тело — авто.")] + public async Task HoleCounterbore( + double x, double y, double z, + [Description("Диаметр отверстия, мм")] double diameter, + [Description("Диаметр цековки, мм (> diameter)")] double spotfaceDiameter, + [Description("Глубина цековки, мм")] double spotfaceDepth, + [Description("Глубина отверстия, мм (если throughAll=false)")] double depth = 0, + [Description("Сквозное отверстие")] bool throughAll = false) + { + await session.ConnectAsync(); + await holes.CounterboreHoleAsync(x, y, z, diameter, depth, throughAll, spotfaceDiameter, spotfaceDepth); + var kind = throughAll ? "сквозное" : $"глубина {depth} мм"; + return $"Отверстие с цековкой Ø{diameter} (цековка Ø{spotfaceDiameter}×{spotfaceDepth}, {kind}) создано в точке ({x}, {y}, {z})."; + } + + [McpServerTool(Name = "hole_countersink")] + [Description("Отверстие с ЗЕНКОВКОЙ (коническое уширение сверху — под винт с потайной головкой) на грани в точке (x,y,z — центр). diameter — диаметр отверстия (мм). depth/throughAll — как у hole. sinkDiameter — диаметр зенковки (мм, больше diameter), sinkAngle — угол конуса зенковки (градусы, 0..180; типично 90). Направление в тело — авто.")] + public async Task HoleCountersink( + double x, double y, double z, + [Description("Диаметр отверстия, мм")] double diameter, + [Description("Диаметр зенковки, мм (> diameter)")] double sinkDiameter, + [Description("Угол конуса зенковки, градусы (0..180, типично 90)")] double sinkAngle, + [Description("Глубина отверстия, мм (если throughAll=false)")] double depth = 0, + [Description("Сквозное отверстие")] bool throughAll = false) + { + await session.ConnectAsync(); + await holes.CountersinkHoleAsync(x, y, z, diameter, depth, throughAll, sinkDiameter, sinkAngle); + var kind = throughAll ? "сквозное" : $"глубина {depth} мм"; + return $"Отверстие с зенковкой Ø{diameter} (зенковка Ø{sinkDiameter}, угол {sinkAngle}°, {kind}) создано в точке ({x}, {y}, {z})."; + } + [McpServerTool(Name = "rebuild")] [Description("Перестроить активный документ.")] public async Task Rebuild() diff --git a/tests/Kompas.Mcp.Tests/Integration/HoleTests.cs b/tests/Kompas.Mcp.Tests/Integration/HoleTests.cs index 46795f9..9f7d00e 100644 --- a/tests/Kompas.Mcp.Tests/Integration/HoleTests.cs +++ b/tests/Kompas.Mcp.Tests/Integration/HoleTests.cs @@ -96,4 +96,55 @@ public sealed class HoleTests } finally { await _docs.CloseAsync(save: false); } } + + [Fact] + public async Task Counterbore_removes_base_plus_spotface() + { + await _docs.CreateAsync(KompasDocumentType.Part); + try + { + var s = await _modeler.OpenSketchAsync(BasePlane.XOY); + await _modeler.AddRectangleAsync(s, -20, -20, 20, 20); + await _modeler.CloseSketchAsync(s); + await _modeler.ExtrudeAsync(s, depth: 20); + var before = (await _query.GetPartInfoAsync()).Volume; + + // Ø10 сквозное + цековка Ø20×5 по центру верхней грани. + await _holes.CounterboreHoleAsync(0, 0, 20, diameter: 10, depth: 0, throughAll: true, + spotDiameter: 20, spotDepth: 5); + var removed = before - (await _query.GetPartInfoAsync()).Volume; + + // База: π·25·20 ≈ 1570.8; + кольцо цековки (π·100−π·25)·5 ≈ 1178 → итого ≈ 2749. + var baseCyl = Math.PI * 25 * 20; + var expected = Math.PI * 25 * 20 + Math.PI * (100 - 25) * 5; + Assert.True(removed > baseCyl * 1.1, "Цековка должна удалить больше, чем простое отверстие."); + Assert.InRange(removed, expected * 0.9, expected * 1.1); + } + finally { await _docs.CloseAsync(save: false); } + } + + [Fact] + public async Task Countersink_removes_base_plus_cone() + { + await _docs.CreateAsync(KompasDocumentType.Part); + try + { + var s = await _modeler.OpenSketchAsync(BasePlane.XOY); + await _modeler.AddRectangleAsync(s, -20, -20, 20, 20); + await _modeler.CloseSketchAsync(s); + await _modeler.ExtrudeAsync(s, depth: 20); + var before = (await _query.GetPartInfoAsync()).Volume; + + // Ø8 сквозное + зенковка Ø16 под 90° по центру верхней грани. + await _holes.CountersinkHoleAsync(0, 0, 20, diameter: 8, depth: 0, throughAll: true, + sinkDiameter: 16, sinkAngle: 90); + var removed = before - (await _query.GetPartInfoAsync()).Volume; + + // База Ø8 сквозь 20 ≈ 1005; зенковка-конус добавляет ~270 (точное зависит от трактовки угла). + var baseCyl = Math.PI * 16 * 20; + Assert.True(removed > baseCyl, "Зенковка должна удалить больше, чем простое отверстие Ø8."); + Assert.True(removed < baseCyl * 2, "Объём зенковки в разумных пределах."); + } + finally { await _docs.CloseAsync(save: false); } + } } From aeb5d58820b933b6da26f1d53ada7b7460088af5 Mon Sep 17 00:00:00 2001 From: Shahovalov MIkhail Date: Wed, 27 May 2026 12:04:28 +0300 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B8?= =?UTF-8?q?=20=D1=82=D0=B8=D0=BF=D0=BE=D0=B2=20=D0=BE=D1=82=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=D1=81=D1=82=D0=B8=D0=B9=20=D0=BF=D0=BE=20=D1=80=D0=B5?= =?UTF-8?q?=D0=B2=D1=8C=D1=8E=20Codex?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - глухая цековка: проверка depth > spotDepth (несовместимая геометрия) — P2 - описание sinkAngle: строго 0 < sinkAngle < 180 (соответствие валидации) — P3 - описание hole_counterbore: требование depth > spotfaceDepth для глухого --- src/Kompas.Mcp.Core/Modeling/HoleService.cs | 2 ++ src/Kompas.Mcp.Host/Tools/FeatureTools.cs | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Kompas.Mcp.Core/Modeling/HoleService.cs b/src/Kompas.Mcp.Core/Modeling/HoleService.cs index ecfa7ce..7aa6a09 100644 --- a/src/Kompas.Mcp.Core/Modeling/HoleService.cs +++ b/src/Kompas.Mcp.Core/Modeling/HoleService.cs @@ -40,6 +40,8 @@ public sealed class HoleService throw new ArgumentOutOfRangeException(nameof(spotDiameter), "Диаметр цековки должен быть больше диаметра отверстия."); if (!double.IsFinite(spotDepth) || spotDepth <= 0) throw new ArgumentOutOfRangeException(nameof(spotDepth), "Глубина цековки должна быть > 0."); + if (!throughAll && double.IsFinite(depth) && depth <= spotDepth) + throw new ArgumentOutOfRangeException(nameof(depth), "Глубина глухого отверстия должна быть больше глубины цековки."); HoleCore(x, y, z, diameter, depth, throughAll, ksHoleTypeEnum.ksHTCounterbore, hole => { var p = hole.HoleParameters as ISpotfacingHoleParameters diff --git a/src/Kompas.Mcp.Host/Tools/FeatureTools.cs b/src/Kompas.Mcp.Host/Tools/FeatureTools.cs index f045718..5f7af1a 100644 --- a/src/Kompas.Mcp.Host/Tools/FeatureTools.cs +++ b/src/Kompas.Mcp.Host/Tools/FeatureTools.cs @@ -213,7 +213,7 @@ public sealed class FeatureTools(KompasSession session, PartModeler modeler, Hol } [McpServerTool(Name = "hole_counterbore")] - [Description("Отверстие с ЦЕКОВКОЙ (цилиндрическое уширение сверху — под винт с цилиндрической головкой) на грани в точке (x,y,z — центр). diameter — диаметр отверстия (мм). depth/throughAll — как у hole. spotfaceDiameter — диаметр цековки (мм, больше diameter), spotfaceDepth — глубина цековки (мм). Направление в тело — авто.")] + [Description("Отверстие с ЦЕКОВКОЙ (цилиндрическое уширение сверху — под винт с цилиндрической головкой) на грани в точке (x,y,z — центр). diameter — диаметр отверстия (мм). depth/throughAll — как у hole (для глухого depth должен быть больше spotfaceDepth). spotfaceDiameter — диаметр цековки (мм, больше diameter), spotfaceDepth — глубина цековки (мм). Направление в тело — авто.")] public async Task HoleCounterbore( double x, double y, double z, [Description("Диаметр отверстия, мм")] double diameter, @@ -229,7 +229,7 @@ public sealed class FeatureTools(KompasSession session, PartModeler modeler, Hol } [McpServerTool(Name = "hole_countersink")] - [Description("Отверстие с ЗЕНКОВКОЙ (коническое уширение сверху — под винт с потайной головкой) на грани в точке (x,y,z — центр). diameter — диаметр отверстия (мм). depth/throughAll — как у hole. sinkDiameter — диаметр зенковки (мм, больше diameter), sinkAngle — угол конуса зенковки (градусы, 0..180; типично 90). Направление в тело — авто.")] + [Description("Отверстие с ЗЕНКОВКОЙ (коническое уширение сверху — под винт с потайной головкой) на грани в точке (x,y,z — центр). diameter — диаметр отверстия (мм). depth/throughAll — как у hole. sinkDiameter — диаметр зенковки (мм, больше diameter), sinkAngle — угол конуса зенковки (градусы, строго 0 < sinkAngle < 180; типично 90). Направление в тело — авто.")] public async Task HoleCountersink( double x, double y, double z, [Description("Диаметр отверстия, мм")] double diameter, From ec5eb2b46d33887fdc56a3044ed479fa56a7ae40 Mon Sep 17 00:00:00 2001 From: Shahovalov MIkhail Date: Wed, 27 May 2026 12:13:23 +0300 Subject: [PATCH 3/3] =?UTF-8?q?docs:=20=D1=82=D0=B8=D0=BF=D1=8B=20=D0=BE?= =?UTF-8?q?=D1=82=D0=B2=D0=B5=D1=80=D1=81=D1=82=D0=B8=D0=B9=20(=D1=86?= =?UTF-8?q?=D0=B5=D0=BA=D0=BE=D0=B2=D0=BA=D0=B0/=D0=B7=D0=B5=D0=BD=D0=BA?= =?UTF-8?q?=D0=BE=D0=B2=D0=BA=D0=B0)=20=E2=80=94=20README/CLAUDE/ARCHITECT?= =?UTF-8?q?URE/OPEN=5FQUESTIONS/presentation;=20=D1=81=D1=87=D1=91=D1=82?= =?UTF-8?q?=D1=87=D0=B8=D0=BA=D0=B8=2069=20=D0=B8=D0=BD=D1=81=D1=82=D1=80?= =?UTF-8?q?=D1=83=D0=BC=D0=B5=D0=BD=D1=82=D0=BE=D0=B2,=20118=20=D1=82?= =?UTF-8?q?=D0=B5=D1=81=D1=82=D0=BE=D0=B2=20(=D0=B8=D1=81=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BE?= =?UTF-8?q?=D1=87=D0=BD=D1=8B=D0=B9=20=D0=BF=D0=BE=D0=B4=D1=81=D1=87=D1=91?= =?UTF-8?q?=D1=82=2089)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CLAUDE.md | 6 +++--- README.md | 6 +++--- docs/ARCHITECTURE.md | 12 +++++++----- docs/OPEN_QUESTIONS.md | 8 +++++++- docs/presentation.html | 17 +++++++++-------- 5 files changed, 29 insertions(+), 20 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 2c0d919..12715a5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -25,7 +25,7 @@ The base is committed to the repo (canonical) — no regeneration step. It cover **v1 + v2 + STEP/assembly + direct B-rep edit + structural model inspection implemented and working** (full sketch→feature→inspect→STEP round-trip + move_face + describe_model validated end-to-end). Stack: **.NET 8 (`net8.0-windows`, x64), C#**, MCP via the official `ModelContextProtocol` SDK over **stdio**. -**67 MCP tools, 116 tests green (69 unit + 47 integration).** v2 added: sketch-on-face (by point and by index), revolve (boss/cut + sketch axis), fillet/chamfer (edge-by-point and by index), and Query tools `get_part_info` (МЦХ), `get_bounding_box`, `list_faces`, `list_edges`. After v2: `import_step`, `export_step` (`ConversionService`, `ConversionTools`), `list_components` (assembly traversal via `TopPart → IParts7`). `move_face` (`FaceEditService`, `EditTools`) — direct B-rep face editing. After move_face: **structural model inspection** — `describe_model`, `list_features`, `list_bodies`, `list_variables`, `describe_face`, `describe_edge`, `measure` (`ModelInspectionService`, `InspectionTools`). `describe_model` is the preferred first call over `model_snapshot` (no token cost for image context; gives feature tree, bodies, variables, topology summary, МЦХ). Package A «richer sketches»: 7 new sketch primitives — `sketch_add_arc`, `sketch_add_arc_3points`, `sketch_add_ellipse`, `sketch_add_polyline`, `sketch_add_polygon`, `sketch_add_spline`, `sketch_add_point`. `PartModeler` refactored into partial classes: `PartModeler.cs` (core), `PartModeler.Sketch.cs` (2D primitives), `PartModeler.Features.cs` (extrude/revolve/fillet/chamfer). Static `SketchGeometry` class in `Core/Modeling`. Package B «forming ops» complete: **`shell`** (оболочка, `o3d_shellOperation=43`) + **`rib`** (ребро жёсткости, `o3d_ribOperation=44`) + **`sweep`** (кинематическая операция, `o3d_baseEvolution=45`) + **`loft`** (по сечениям, `o3d_baseLoft=30`). Auxiliary geometry (start of package E): **`sketch_create_on_offset_plane`** (смещённая плоскость, `o3d_planeOffset=14`) — unlocked `loft`. Additional Edit tools: **`split_solid_by_plane`**, **`move_body`**, **`boolean_union`**. Package C «patterns & mirror» complete: **`linear_pattern`** (линейный массив, `o3d_meshCopy=35`) + **`circular_pattern`** (круговой массив, `o3d_circularCopy=36`) + **`mirror_operation`** (зеркальная копия операций, `o3d_mirrorOperation=48`) + **`mirror_body`** (зеркало тела, `o3d_mirrorAllOperation=49`). **`hole`** (`HoleService`, `FeatureTools`) — цилиндрическое отверстие (сквозное или глухое) через **API7** (`IModelContainer`/`IHole3D`/`IHoleDisposal`); первая формообразующая операция на API7 (прецедент ранее только у `move_face`). **`draft`** (уклон, `o3d_incline=42`) — наклон граней на угол относительно нейтральной координатной плоскости; реализован через **API5** `ksInclineDefinition` (операция называется Incline в API5, не Draft). Package D «parametrics» started: **`create_variable`**, **`set_variable`**, **`delete_variable`** (`VariableService`, `VariableTools`) — CRUD переменных модели через API5. `list_variables` исправлен (читает `ksPart.GetFeature().VariableCollection`, а не `ksPart.VariableCollection()`). **Ограничение:** переменная управляет геометрией только в параметрической модели (где размеры эскиза связаны с именем переменной); наши эскизы строятся литеральными координатами → `set_variable` хранит/вычисляет значение, но геометрию не меняет. Связь эскизных размеров с переменными (API2D) — не реализована. +**69 MCP tools, 118 tests green (69 unit + 49 integration).** v2 added: sketch-on-face (by point and by index), revolve (boss/cut + sketch axis), fillet/chamfer (edge-by-point and by index), and Query tools `get_part_info` (МЦХ), `get_bounding_box`, `list_faces`, `list_edges`. After v2: `import_step`, `export_step` (`ConversionService`, `ConversionTools`), `list_components` (assembly traversal via `TopPart → IParts7`). `move_face` (`FaceEditService`, `EditTools`) — direct B-rep face editing. After move_face: **structural model inspection** — `describe_model`, `list_features`, `list_bodies`, `list_variables`, `describe_face`, `describe_edge`, `measure` (`ModelInspectionService`, `InspectionTools`). `describe_model` is the preferred first call over `model_snapshot` (no token cost for image context; gives feature tree, bodies, variables, topology summary, МЦХ). Package A «richer sketches»: 7 new sketch primitives — `sketch_add_arc`, `sketch_add_arc_3points`, `sketch_add_ellipse`, `sketch_add_polyline`, `sketch_add_polygon`, `sketch_add_spline`, `sketch_add_point`. `PartModeler` refactored into partial classes: `PartModeler.cs` (core), `PartModeler.Sketch.cs` (2D primitives), `PartModeler.Features.cs` (extrude/revolve/fillet/chamfer). Static `SketchGeometry` class in `Core/Modeling`. Package B «forming ops» complete: **`shell`** (оболочка, `o3d_shellOperation=43`) + **`rib`** (ребро жёсткости, `o3d_ribOperation=44`) + **`sweep`** (кинематическая операция, `o3d_baseEvolution=45`) + **`loft`** (по сечениям, `o3d_baseLoft=30`). Auxiliary geometry (start of package E): **`sketch_create_on_offset_plane`** (смещённая плоскость, `o3d_planeOffset=14`) — unlocked `loft`. Additional Edit tools: **`split_solid_by_plane`**, **`move_body`**, **`boolean_union`**. Package C «patterns & mirror» complete: **`linear_pattern`** (линейный массив, `o3d_meshCopy=35`) + **`circular_pattern`** (круговой массив, `o3d_circularCopy=36`) + **`mirror_operation`** (зеркальная копия операций, `o3d_mirrorOperation=48`) + **`mirror_body`** (зеркало тела, `o3d_mirrorAllOperation=49`). **`hole`** (`HoleService`, `FeatureTools`) — цилиндрическое отверстие (сквозное или глухое) через **API7** (`IModelContainer`/`IHole3D`/`IHoleDisposal`); первая формообразующая операция на API7 (прецедент ранее только у `move_face`). **`hole_counterbore`** — отверстие с цековкой (`ksHTCounterbore`, `ISpotfacingHoleParameters`). **`hole_countersink`** — отверстие с зенковкой (`ksHTCountersinking`, `ICountersinkHoleParameters`). `HoleCore` рефакторен: принимает `ksHoleTypeEnum holeType` + `Action configure` (настройка `HoleParameters` после установки `HoleType`, т.к. параметрический подынтерфейс недоступен до фиксации типа). Базовый `hole` = `ksHTBase` + `configure=null`. **`draft`** (уклон, `o3d_incline=42`) — наклон граней на угол относительно нейтральной координатной плоскости; реализован через **API5** `ksInclineDefinition` (операция называется Incline в API5, не Draft). Package D «parametrics» started: **`create_variable`**, **`set_variable`**, **`delete_variable`** (`VariableService`, `VariableTools`) — CRUD переменных модели через API5. `list_variables` исправлен (читает `ksPart.GetFeature().VariableCollection`, а не `ksPart.VariableCollection()`). **Ограничение:** переменная управляет геометрией только в параметрической модели (где размеры эскиза связаны с именем переменной); наши эскизы строятся литеральными координатами → `set_variable` хранит/вычисляет значение, но геометрию не меняет. Связь эскизных размеров с переменными (API2D) — не реализована. See [`README.md`](README.md) and [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md); design decisions/caveats in [`docs/OPEN_QUESTIONS.md`](docs/OPEN_QUESTIONS.md). @@ -53,7 +53,7 @@ Key implementation facts (don't relearn): - **Structural model inspection** (`ModelInspectionService`, `InspectionTools`): `describe_model` — one-call structural "passport" (bounding box + МЦХ + bodies + topology summary + feature tree + variables); preferred over snapshot. `list_features` — feature tree with params (depth/radius/legs). `list_bodies` — list solid/surface bodies with face count. `list_variables` — model variables (name/expression/value, external/info flags); reads via `ksPart.GetFeature().VariableCollection` (all variables, including user-created) with fallback to `ksPart.VariableCollection()` (external-only — bug was fixed). `describe_face` — drill-down by index (type, area, normal, radius, edge count). `describe_edge` — drill-down by index (type, length, adjacent faces, vertices). `measure` — distance/angle between two objects (face|edge|vertex by index, unit `ST_MIX_MM`). - **Variable management (package D start, `VariableService`, `VariableTools`)**: `create_variable(name, value, note?, external?)` — creates a model variable; `set_variable(name, expression)` — sets expression (constant `"30"` or formula `"width*2+5"`); `delete_variable(name)` — deletes (fails if dependents exist). **Key API facts (don't relearn):** variable creation MUST use `ksPart.GetFeature().VariableCollection` (property on root ksFeature) — NOT `ksPart.VariableCollection()` (returns external-only). Expression is the leading field; value is derived. Changes applied via `ksPart.RebuildModel()` (return checked); after rebuild the RCW variable is stale → re-read via fresh collection (`ReadValueFresh`). Dependent variables recalculate automatically. Deletion fails if dependents exist (RemoveVariable returns FALSE). **Geometry limitation:** variables control geometry only in a parametric model where sketch dimensions are linked to variable names; our sketches are built with literal coordinates → `set_variable` stores/computes the value but does NOT change geometry. Linking sketch dimensions to variables (parametric sketches via API2D) is not yet implemented. - **Feature tree API** (don't relearn): read via API5 `ksPart.GetFeature()` → `(ksFeature).SubFeatureCollection(true,false)` → `ksFeatureCollection`. Cast `(ksFeature)part` does NOT work (RCW QI returns null) — must use `GetFeature()`. `ksFeature.type` returns only coarse `o3d_entity` and does NOT distinguish operations; precise type and params come from `ksFeature.GetObject()` → `ksEntity.GetDefinition()` and matching definition type (`ksBossExtrusionDefinition.GetSideParam`, `ksFilletDefinition.radius`, `ksChamferDefinition.GetChamferParam`, …). Node name ("Элемент выдавливания:1", "Скругление:1") is localized by КОМПАС itself. -- **STEP import/export, `list_components`, `move_face`, `split_solid_by_plane`, `move_body`, `boolean_union`, full inspection layer, richer sketch primitives (package A), `shell`, `rib`, `sweep`, `loft` (package B complete), `sketch_create_on_offset_plane` (package E start), `linear_pattern`, `circular_pattern`, `mirror_operation`, `mirror_body` (package C complete), `hole` (API7), `draft` (API5 `ksInclineDefinition`), `create_variable`/`set_variable`/`delete_variable` (package D start) are done.** Not yet done: parametric sketches (link sketch dimensions to variable names via API2D — package D continuation), 2D drawing, assembly building; package E continuation (axis, angle plane). Known caveat: boss/cut direction on a selected face depends on face-normal orientation (`forward` may need flipping — agent picks via snapshot or `describe_face` normal). See `docs/OPEN_QUESTIONS.md` → «Ревью v2». +- **STEP import/export, `list_components`, `move_face`, `split_solid_by_plane`, `move_body`, `boolean_union`, full inspection layer, richer sketch primitives (package A), `shell`, `rib`, `sweep`, `loft` (package B complete), `sketch_create_on_offset_plane` (package E start), `linear_pattern`, `circular_pattern`, `mirror_operation`, `mirror_body` (package C complete), `hole`, `hole_counterbore`, `hole_countersink` (all API7 via `HoleService`), `draft` (API5 `ksInclineDefinition`), `create_variable`/`set_variable`/`delete_variable` (package D start) are done.** Not yet done: parametric sketches — **investigated, unavailable via COM API** (`ksCDimWithVariable` cannot be constructed from external automation; see `docs/superpowers/specs/2026-05-27-parametric-sketch-findings.md`); 2D drawing, assembly building; package E continuation (axis, angle plane). Known caveat: boss/cut direction on a selected face depends on face-normal orientation (`forward` may need flipping — agent picks via snapshot or `describe_face` normal). See `docs/OPEN_QUESTIONS.md` → «Ревью v2». - **"STEP import without history"** detected structurally: `bodyCount > 0 && no formative features && features.Count <= bodyCount+1` (only origin + body). STEP with edits (move_face: "Смещённая плоскость", "Разрезать", "Переместить грани", "Булева операция") — that already has history. - **Topology/measure API**: `ksFaceDefinition.EdgeCollection` / `GetCylinderParam` / `GetSurface().GetNormal` + `normalOrientation`; `ksEdgeDefinition.GetAdjacentFace(bool)` / `GetVertex(bool)` → `ksVertexDefinition.GetPoint`; bodies via `ksPart.BodyCollection()` → `ksBody` (`IsSolid` / `FaceCollection`); all variables via `ksPart.GetFeature().VariableCollection` → `ksVariable` (external-only: `ksPart.VariableCollection()` — do NOT use for reading all variables); measurements via `ksPart.GetMeasurer()` → `ksMeasurer` (`SetObject1/2`, `unit=ST_MIX_MM`, `Calc`, `distance`/`MinDistance`/`angle` in degrees / `IsAngleValid`). - **STEP import COM pattern** (verified): `IApplication.get_Converter((object)(int)ksConverterFromSTEP=-3)` (pass format code, not DLL path) → `IConverter.ConverterParameters(cmd)` → set `IAdditionConvertParameters.Format=ksConverterFromSTEP` → `IKompasDocument3D1.ConvertFromAdditionFormat(path, prm)`. Export: `ConvertToAdditionFormat` with format codes for AP203/AP214/AP242. Param co-classes (AdditionConvertParameters etc.) are NOT CoCreatable — only via converter factories. Assembly traversal: `IKompasDocument3D.TopPart → IPart7.Parts (IParts7)`. Extract component: `prm.NeedCreateComponentsFiles=true` (writes .m3d files next to STEP) + `IPart7.OpenSourceDocument`. @@ -64,7 +64,7 @@ Key implementation facts (don't relearn): - **Offset plane (`sketch_create_on_offset_plane`, package E start)**: `OpenSketchOnOffsetPlaneAsync` in `PartModeler.Sketch.cs`. `ksPart.NewEntity(o3d_planeOffset=14)` → `ksPlaneOffsetDefinition` (`SetPlane(basePlane)` + `offset` mm + `direction`) → `Create()` → sketch on that plane. Used to place sketches at arbitrary heights — enables `loft` with parallel sections. `entity.Create()` result is checked (reliability fix applied to all `CreateSketchOn*` paths). - **Loft operation (`loft`, package B)**: `ksBaseLoftDefinition` via `ksPart.NewEntity(o3d_baseLoft=30)`. Params: `sketchIds[]` (≥2 closed profile sketches on different parallel planes). `Sketchs().Add(each sketch)` → `SetLoftParam(closed=false, false, true)` → `SetThinParam(false)` → `Create()`. Requires parallel section sketches at different heights — use `sketch_create_on_offset_plane` for the non-base sections. - **Patterns & mirror (package C)**: `linear_pattern` — `ksMeshCopyDefinition` via `ksPart.NewEntity(o3d_meshCopy=35)`; `SetAxis1(axis)` + `SetCopyParamAlongAxis(true,0,count,step,false)` + `count2=1` (disables 2nd direction → 1D linear); features added via `OperationArray()` (no Get). `circular_pattern` — `ksCircularCopyDefinition` via `NewEntity(o3d_circularCopy=36)`; `SetAxis(axis)`; properties set directly: `count1=1` (radial off), `count2=count` (ring), `step2=angle°` between neighbours, `factor2=false`, `inverce=reverse`; `GetOperationArray().Add(feature)`. `mirror_operation` — `ksMirrorCopyDefinition` via `NewEntity(o3d_mirrorOperation=48)`; `SetPlane(basePlane)` + `GetOperationArray().Add(feature)`. `mirror_body` — `ksMirrorCopyAllDefinition` via `NewEntity(o3d_mirrorAllOperation=49)`; `SetPlane(basePlane)`; `ChooseBodies()` does NOT cast to `ksChooseBodies` (returns null) and is not needed — `o3d_mirrorAllOperation` mirrors all bodies keeping the original (volume doubles). Source features retrieved by id from `_features` registry via new private `RequireFeature` helper (analogous to `RequireSketch`). New `src/Kompas.Mcp.Core/Modeling/CoordinateAxis.cs` — enum `CoordinateAxis {X,Y,Z}` + `CoordinateAxes.ToObj3dType/Parse` (→ `o3d_axisOX/OY/OZ = 71/72/73`), analogous to `BasePlane.cs`. `SketchGeometry.RequireMin(int value, int min, string paramName)` — validator for `count>=2`. Empirical: `count` includes the original instance (count=3 → 3 bodies); `count2=1` disables 2nd direction for linear; `step2` is angle° between neighbours for circular. -- **Hole operation (`hole`)**: implemented via **API7** (not API5 — `ksHoleDefinition` absent in interop, analogous to `draft`). New service `src/Kompas.Mcp.Core/Modeling/HoleService.cs` (NOT a `PartModeler` method). Tool signature: `hole(x, y, z, diameter, depth=0, throughAll=false)` — simple cylindrical hole (`ksHTBase`), blind or through-all, placed on the face found by world point. Returns confirmation text (no id — hole does NOT enter the `_features` API5 registry). Implementation: `(IModelContainer)part7` (COM-QI) → `Points3D.Add()` creates `IPoint3D` at world coords (centre point) → `Holes3D.Add()` → `IHole3D` (`HoleType=ksHTBase`, `Diameter`, `DepthType=ksDTReachThrough|ksDTValue`, `Depth`, `EndFaceType=ksEFFlat`) → `(IHoleDisposal)hole`: `BaseSurface=face` (via `FindObjectsByPoint`, same as `FaceEditService`), `Perpendicular=true`, `AssociationVertex=point` → `Update()` → `RebuildDocument()`. Placement via `IHoleDisposal` (no position property on `IHole3D`); no placement sketch needed (unlike `extrude_cut`). **Direction selection by volume delta**: `Update()` returns TRUE even when drilling outward (into air, 0 material removed). Strategy: try `Direction=true` → Rebuild → compare volume via API5 `CalcMassInertiaProperties`; if volume did not decrease → try `Direction=false`. Same class of problem as boss/cut face-normal ambiguity. On failure — roll back the orphan point and hole via `IFeature7.Delete()`. Input validation includes `double.IsFinite` check (Infinity passed `>0` guard). +- **Hole operations (`hole`, `hole_counterbore`, `hole_countersink`)**: all implemented via **API7** (not API5 — `ksHoleDefinition` absent in interop). Service `src/Kompas.Mcp.Core/Modeling/HoleService.cs`. `HoleCore` helper is parameterised with `ksHoleTypeEnum holeType` + `Action configure` callback (configure sets `HoleParameters` **after** `HoleType` is assigned, because the type-specific sub-interface — e.g. `ISpotfacingHoleParameters` — is not accessible until the type is fixed). Base `hole` = `ksHTBase` + `configure=null`. `hole_counterbore`: `ksHTCounterbore` + configure sets `(ISpotfacingHoleParameters)hole.HoleParameters` (`SpotfacingDiameter`, `SpotfacingDepth`); validates `spotDiameter>diameter` and `depth>spotDepth` for blind. `hole_countersink`: `ksHTCountersinking` + configure sets `(ICountersinkHoleParameters)hole.HoleParameters` (`CountersinkType=ksCTDiameterAngle`, `CountersinkDiameter`, `CountersinkAngle`); validates `sinkDiameter>diameter`, `0/reset), `PartModeler.Sketch.cs` (all 2D primitives), `PartModeler.Features.cs` (extrude/revolve/fillet/chamfer). Static `SketchGeometry` class (`Core/Modeling`) holds enum mappings (`ArcDirection`, `PolygonDescribe`) and validators (`RequirePositive`, `RequireVertexCount`, `RequirePoints`). Polyline/spline point lists use `record SketchPoint(X, Y)` with JSON names `x`/`y`. diff --git a/README.md b/README.md index 0a682fe..3f6f4d2 100644 --- a/README.md +++ b/README.md @@ -39,14 +39,14 @@ src/Kompas.Mcp.Host/bin/Release/net8.0-windows/kompas-mcp.exe } ``` -## Инструменты (67 инструментов) +## Инструменты (69 инструментов) | Группа | Инструменты | |---|---| | System | `kompas_connect`, `kompas_status`, `kompas_set_visible` | | Documents | `document_create`, `document_open`, `document_save`, `document_save_as`, `document_close`, `document_active` | | Sketch | `sketch_create`, `sketch_create_on_face`, `sketch_create_on_face_index`, `sketch_create_on_offset_plane`, `sketch_add_line`, `sketch_add_circle`, `sketch_add_rectangle`, `sketch_add_axis`, `sketch_add_arc`, `sketch_add_arc_3points`, `sketch_add_ellipse`, `sketch_add_polyline`, `sketch_add_polygon`, `sketch_add_spline`, `sketch_add_point`, `sketch_close` | -| Features | `extrude_boss`, `extrude_cut`, `revolve_boss`, `revolve_cut`, `fillet_edge`, `chamfer_edge`, `fillet_edge_index`, `chamfer_edge_index`, `shell`, `rib`, `sweep`, `loft`, `linear_pattern`, `circular_pattern`, `mirror_operation`, `mirror_body`, `hole`, `draft`, `rebuild` | +| Features | `extrude_boss`, `extrude_cut`, `revolve_boss`, `revolve_cut`, `fillet_edge`, `chamfer_edge`, `fillet_edge_index`, `chamfer_edge_index`, `shell`, `rib`, `sweep`, `loft`, `linear_pattern`, `circular_pattern`, `mirror_operation`, `mirror_body`, `hole`, `hole_counterbore`, `hole_countersink`, `draft`, `rebuild` | | Edit | `move_face`, `split_solid_by_plane`, `move_body`, `boolean_union` | | Inspection | `describe_model`, `list_features`, `list_bodies`, `list_variables`, `describe_face`, `describe_edge`, `measure` | | Vision | `model_snapshot` (PNG-снимок; fallback — сначала `describe_model`) | @@ -71,7 +71,7 @@ src/Kompas.Mcp.Host/bin/Release/net8.0-windows/kompas-mcp.exe ``` src/Kompas.Mcp.Core/ COM-слой: STA-диспетчер, подключение, документы, эскизы/операции, конвертация, снимок, инспекция модели src/Kompas.Mcp.Host/ MCP-сервер (stdio) + определения инструментов -tests/Kompas.Mcp.Tests/ 116 тестов: unit + integration (integration требуют КОМПАС) +tests/Kompas.Mcp.Tests/ 118 тестов: unit + integration (integration требуют КОМПАС) libs/kompas-interop/ вендорские interop-сборки КОМПАС (из SDK Samples/Common) docs/ архитектура, план, презентация, MD-база знаний SDK (Kompas3D_SDK/) usecases/ полигон обкатки подходов (в .gitignore); приёмы поднимаются в навык kompas-3d diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 89c28bc..d333db5 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -1,6 +1,6 @@ # Архитектура MCP-сервера КОМПАС-3D (предлагаемый вариант) -> Статус: **реализовано и работает** (v1+v2+STEP/assembly+direct-edit+inspection+package-A «богатые эскизы»+package-B «shell+rib+sweep+loft»+package-C «массивы и зеркало»+package-D start «переменные»+package-E «offset plane»+`hole`+`draft`). Актуализировано по коду. +> Статус: **реализовано и работает** (v1+v2+STEP/assembly+direct-edit+inspection+package-A «богатые эскизы»+package-B «shell+rib+sweep+loft»+package-C «массивы и зеркало»+package-D start «переменные»+package-E «offset plane»+`hole`+`hole_counterbore`+`hole_countersink`+`draft`). Актуализировано по коду. > Сопутствующий контекст по COM API КОМПАС — в [`../CLAUDE.md`](../CLAUDE.md). > Имена интерфейсов и перечислений сверены со справкой SDK по MD-базе знаний `docs/Kompas3D_SDK/` (навык `kompas-sdk-research`). @@ -113,7 +113,7 @@ MCP client ──stdio──> Host (поток-пул, async) |---|---| | **Kompas.Mcp.Host** | Точка входа. Сборка MCP-сервера (stdio), DI (в т.ч. `ConversionService`), логи в stderr, старт и владение STA-потоком, graceful shutdown. Определения инструментов по категориям (System / Documents / Sketch / Features / Query / Conversion). **Тонкий слой**: валидация аргументов → постановка задачи на STA-поток → форматирование результата/ошибки в MCP-ответ. | | **Kompas.Mcp.Core** | COM-слой. Менеджер соединения, менеджер документов, построитель эскизов и операций, `ConversionService` (STEP импорт/экспорт), `QueryService` (МЦХ, грани, рёбра, компоненты), `ModelInspectionService` (дерево операций, тела, переменные, drill-down, измерения), снимок. Все паттерны API5/API7 живут здесь. | -| **Kompas.Mcp.Tests** | Юнит (без COM) + интеграционные (требуют КОМПАС). 116 тестов (69 unit + 47 integration). | +| **Kompas.Mcp.Tests** | Юнит (без COM) + интеграционные (требуют КОМПАС). 118 тестов (69 unit + 49 integration). | Принцип: **только `Core` знает про COM**. `Host`/Tools оперируют доменными DTO и вызывают `Core`; `Host` не содержит бизнес-логики. @@ -121,7 +121,7 @@ MCP client ──stdio──> Host (поток-пул, async) --- -## 5. Карта инструментов (67 инструментов) +## 5. Карта инструментов (69 инструментов) Сгруппированы вокруг центрального цикла «эскиз ↔ операция». Имена — `snake_case`. @@ -157,6 +157,8 @@ MCP client ──stdio──> Host (поток-пул, async) - `mirror_operation` — зеркальная копия операций (`o3d_mirrorOperation=48`, `ksMirrorCopyDefinition`); параметры: `featureIds[]`, `plane` (XOY/XOZ/YOZ). `SetPlane` + `GetOperationArray()`. - `mirror_body` — зеркало всего тела (`o3d_mirrorAllOperation=49`, `ksMirrorCopyAllDefinition`); параметр: `plane`. `ChooseBodies()` не приводится к `ksChooseBodies` и не нужен — операция отражает все тела, сохраняя оригинал (объём удваивается). - `hole` — цилиндрическое отверстие (сквозное или глухое) на грани, найденной по мировой точке (x,y,z). Параметры: `diameter` (мм), `depth` (мм, для глухого), `throughAll`. Реализовано через **API7** (`HoleService`, `src/Kompas.Mcp.Core/Modeling/HoleService.cs`) — в API5 интерфейс `ksHoleDefinition` отсутствует в interop. Паттерн: `(IModelContainer)part7` → `Points3D.Add()` (центр, `IPoint3D`) → `Holes3D.Add()` → `IHole3D` (`HoleType=ksHTBase`, `Diameter`, `DepthType`, `Depth`, `EndFaceType=ksEFFlat`) → `(IHoleDisposal)hole` (`BaseSurface=грань` через `FindObjectsByPoint`, `Perpendicular=true`, `AssociationVertex=point`) → `Update()` → `RebuildDocument()`. Направление сверления определяется **по факту убыли объёма**: `Direction=true` → Rebuild → сравнить объём (API5 `CalcMassInertiaProperties`); если не убыл → `Direction=false`. При неуспехе — откат через `IFeature7.Delete()`. Возвращает подтверждение без id (отверстие не попадает в реестр `_features` API5). +- `hole_counterbore` — отверстие с цековкой (цилиндрическое уширение сверху, под винт с цилиндрической головкой). Параметры: `x,y,z`, `diameter`, `spotfaceDiameter` (>diameter), `spotfaceDepth`, `depth=0`, `throughAll=false`. `HoleType=ksHTCounterbore`; `HoleParameters` приводится к `ISpotfacingHoleParameters` (`SpotfacingDiameter`, `SpotfacingDepth`) — настройка через `configure`-колбэк после фиксации типа. Для глухого: валидируется `depth>spotDepth`. +- `hole_countersink` — отверстие с зенковкой (коническое уширение сверху, под винт с потайной головкой). Параметры: `x,y,z`, `diameter`, `sinkDiameter` (>diameter), `sinkAngle` (0
- Статус: v3+ работает — эскиз→операции→STEP→сборка→move_face→структурный осмотр→богатые эскизы→shell+rib+sweep+loft+массивы+зеркало+hole+draft+переменные (67 инструментов) + Статус: v3+ работает — эскиз→операции→STEP→сборка→move_face→структурный осмотр→богатые эскизы→shell+rib+sweep+loft+массивы+зеркало+hole+hole_counterbore+hole_countersink+draft+переменные (69 инструментов)

КОМПАС-3D MCP-сервер
управление CAD языком LLM

MCP-сервер, который превращает операции КОМПАС-3D — создание документов, эскизы, 3D-операции, параметры — в инструменты для языковой модели. Под капотом — @@ -261,7 +261,7 @@ впереди.

-
Общий прогрессv3+: STEP · move_face · структурный осмотр · богатые эскизы (пакет A) · shell+rib+sweep+loft (пакет B) · массивы+зеркало (пакет C) · переменные (пакет D start) · offset-plane (пакет E start) · hole (API7) · draft (API5) · навык kompas-3d · 67 инструментов · 116 тестов
+
Общий прогрессv3+: STEP · move_face · структурный осмотр · богатые эскизы (пакет A) · shell+rib+sweep+loft (пакет B) · массивы+зеркало (пакет C) · переменные (пакет D start) · offset-plane (пакет E start) · hole (API7) · hole_counterbore · hole_countersink · draft (API5) · навык kompas-3d · 69 инструментов · 118 тестов
@@ -297,19 +297,19 @@

Инструменты v3+ ГОТОВО

-

67 инструментов, отдаются по MCP-протоколу

+

69 инструментов, отдаются по MCP-протоколу

  • System: connect · status · set_visible
  • Documents: create · open · save · save_as · close · active
  • Sketch: create · on_face (точка/индекс) · on_offset_plane (смещённая плоскость) · add line/circle/arc/arc_3points/rectangle/axis/ellipse/polyline/polygon/spline/point · close
  • -
  • Features: extrude · revolve · fillet_edge/chamfer_edge (точка + индекс) · shell (оболочка) · rib (ребро жёсткости) · sweep (кинематическая) · loft (по сечениям) · linear_pattern · circular_pattern · mirror_operation · mirror_body · hole (API7) · draft (уклон, API5) · rebuild
  • +
  • Features: extrude · revolve · fillet_edge/chamfer_edge (точка + индекс) · shell (оболочка) · rib (ребро жёсткости) · sweep (кинематическая) · loft (по сечениям) · linear_pattern · circular_pattern · mirror_operation · mirror_body · hole (API7) · hole_counterbore (цековка, API7) · hole_countersink (зенковка, API7) · draft (уклон, API5) · rebuild
  • Edit: move_face · split_solid_by_plane · move_body · boolean_union
  • Inspection: describe_model · list_features · list_bodies · list_variables · describe_face · describe_edge · measure
  • Vision: model_snapshot 👁️ (fallback)
  • Query: get_part_info · get_bounding_box · list_faces · list_edges · list_components
  • Variables: create_variable · set_variable · delete_variable (пакет D)
  • Conversion: import_step · export_step
  • -
  • пакет D продолжение (параметрические эскизы, API2D) · пакет E (ось, угловая плоскость)
  • +
  • пакет E (ось, угловая плоскость) · пакет D параметрика: параметрические эскизы (API2D) — исследовано, недоступно через COM API
@@ -320,7 +320,7 @@
  • Unit-тесты — без COM: диспетчер, enum-маппинг, StepFormat, InspectionText
  • Интеграционные тесты с КОМПАС: подключение, документы, снимок, цикл, STEP round-trip, сборка, move_face, инспекция
  • -
  • Итого 116 тестов зелёных (69 unit + 47 integration)
  • +
  • Итого 118 тестов зелёных (69 unit + 49 integration)
  • Сборка kompas-mcp.exe, README с конфигом клиента
  • Навык kompas-3d (методика) + полигон usecases/
  • In-process тест MCP-клиента, релизный publish
  • @@ -345,8 +345,9 @@
    пакет B · готово ✓

    Формообразующие операции

    shell ✓ · rib ✓ · sweep ✓ · loft ✓ (по сечениям) · offset-plane ✓ (пакет E)

    пакет C · готово ✓

    Массивы и зеркало

    linear_pattern ✓ · circular_pattern ✓ · mirror_operation ✓ · mirror_body ✓ · hole ✓ (API7)

    draft · готово ✓

    Уклон

    draft ✓ (API5 ksInclineDefinition, o3d_incline=42) · 64 инструмента · 115 тестов

    -
    пакет D start · готово ✓

    Переменные модели

    create_variable · set_variable · delete_variable (API5 VariableService) · list_variables исправлен · 67 инструментов · 116 тестов

    -
    далее · в работе

    пакет D продолжение + пакет E

    параметрические эскизы (API2D) · вспомогательная геометрия (ось, угловая плоскость)

    +
    пакет D start · готово ✓

    Переменные модели

    create_variable · set_variable · delete_variable (API5 VariableService) · list_variables исправлен · 67 инструментов

    +
    отверстия под крепёж · готово ✓

    Цековка и зенковка

    hole_counterbore (ksHTCounterbore) · hole_countersink (ksHTCountersinking) · HoleCore рефакторен (configure-колбэк) · 69 инструментов · 118 тестов

    +
    далее · в работе

    пакет E продолжение

    вспомогательная геометрия (ось, угловая плоскость) · пакет D параметрика — параметрические эскизы недоступны через COM API