Files
kompas3d-mcp/docs/OPEN_QUESTIONS.md
T
mikhail 28d2ae3868 feat(inspection): структурное «зрение» модели — осмотр без снимка
Новый слой «модель как данные» (приоритетный над model_snapshot):
- describe_model — структурный «паспорт» детали одним вызовом (габарит,
  МЦХ, тела, сводка топологии, дерево построения с параметрами, переменные)
- list_features / list_bodies / list_variables
- describe_face / describe_edge (drill-down по индексу)
- measure (расстояние/угол между гранями/рёбрами/вершинами)
- model_snapshot понижен до fallback для визуально-пространственных вопросов

Реализация: ModelInspectionService (COM) + чистый InspectionText (рендер,
группировка, детект «импорт без истории») + record-модели; InspectionTools.
Дерево читается через ksPart.GetFeature()->SubFeatureCollection; точный тип
и параметры операций берутся из GetObject()->GetDefinition() (ksFeature.type
отдаёт лишь coarse o3d_entity).

Снимок дерева также включает слои conversion (STEP), editing (move_face/solid)
и validation, развивавшиеся параллельно. Полный прогон: 61 тест зелёный,
43 MCP-инструмента. Документация (README, ARCHITECTURE, OPEN_QUESTIONS,
CLAUDE.md, presentation.html) синхронизирована.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 18:44:38 +03:00

15 KiB
Raw Blame History

Открытые вопросы для проработки

Журнал спорных моментов и решений, принятых автономно (ночная сессия 2026-05-25/26). Помечено: ⚠️ = требует вашего решения; = решено по умолчанию, можно пересмотреть.


⚠️ 1. API5 vs API7 для построения 3D

Контекст. Примеры SDK строят 3D через API5 (ksPart.NewEntity(o3d_*)ksBossExtrusionDefinitionCreate()). API7 предлагает IPart7IModelContainerIExtrusions.Add(). Решение по умолчанию ( в коде): в v1 строю 3D через API5 ksPart (надёжно, повторяет рабочие примеры Step3d1). API7 использую для приложения/документов/версии. На проработку: переводить ли операции на чистый API7 позже (плюс — единообразие, минус — риск).

2. Точка входа подключения — API5

Для построения 3D нужен KompasObject (API5), а для снимка — ksDocument3D (API5). Поэтому KompasSession подключается через KOMPAS.Application.5KompasObject, затем ksGetApplication7()IApplication. Держит обе ссылки. (Ранее в спайке заходили через .7.)

3. Состав проектов v1

Вместо 4 проектов (Host/Tools/Core/Interop) сделано компактно: Core (COM + сервисы) + Host (MCP + определения инструментов) + Tests. Interop — вендорские DLL в libs/. Разнести на больше проектов можно позже.

4. Тестовые артефакты

Тестовые документы КОМПАС сохраняю в .scratch/ (gitignore). Снимки рендера — туда же, для визуальной проверки.


Само-ревью кода (ночная сессия) — итоги

Исправлено сразу:

  • C1/C2 — утечки COM и устаревшие id: PartModeler освобождает RCW и сбрасывает реестр (ResetAsync/Dispose), DocumentTools сбрасывает реестр при create/open/close; DocumentService освобождает транзитные IDocuments/IKompasDocument.
  • M3 — KompasSession.Dispose: ограниченное ожидание (5 c) вместо вечного блока.
  • M4 — ExtrudeAsync: убран dynamic, конкретные ksBossExtrusionDefinition/ ksCutExtrusionDefinition; throughAll разрешён только для выреза.
  • m7 — extrude_cut: дефолт throughAll=false, без молчаливой подмены depth.
  • Регрессионный тест: после ResetAsync старый id эскиза недействителен.

⚠️ Отложено на проработку (из ревью):

  • M5 — отмена/насос сообщений: CancellationToken не прерывает уже идущий COM-вызов; зависший вызов (модальный диалог КОМПАС) блокирует всю очередь. Возможное решение — диспетчер с насосом сообщений + подавление диалогов. Связано с [конкурентностью MCP].
  • m6 — colorBPP=24 для снимка: для GIF (палитра) может не подойти; снимок по умолчанию PNG.
  • m8 — приведения enum к short для GetPart/NewEntity/GetDefaultEntity: на v24 работает (тесты зелёные), но стоит сверить точные типы параметров в interop.
  • m10 — _app/_kompas читаются из IsConnected/Status() вне STA-потока без volatile (доброкачественное устаревшее чтение).
  • m11 — трансляция COMException/HRESULT в понятные сообщения (сейчас понятны только наши InvalidOperationException); добавить текст последней ошибки КОМПАС.

Ревью v2 (грань/вращение/скругление/фаска/габариты/список граней)

⚠️ Отложено:

  • v2-1 — направление операций на грани: для бобышки/выреза на выбранной грани параметр forward зависит от ориентации нормали грани, которая для эскиза-на-грани оказывается направленной внутрь тела (boss с forward=true уходит в материал — прироста нет; наружу даёт forward=false). Сейчас выбор направления — на стороне агента (по снимку). На проработку: автоопределение «наружу» по нормали грани, либо опция auto.
  • v2-2 — утечки транзитных RCW в PartModeler: GetTopPart() и коллекции EntityCollection(o3d_face/o3d_edge) не освобождаются (унаследованный паттерн; QueryService их освобождает, PartModeler — только зарегистрированные сущности в ResetCore). Накапливается за длинную сессию. На проработку: единый using/release транзитов в построителе.
  • v2-3 — стабильность индексов граней (list_facessketch_create_on_face_index): индекс валиден, пока геометрия не меняется между вызовами. После любой операции порядок/состав граней может измениться — переиспользовать старый индекс нельзя, нужно перезапросить list_faces.
  • v2-4 — fillet/chamfer строятся по одному ребру (первое из SelectByPoint/коллекции); tangent=false. На проработку: набор рёбер, скругление по касательным, переменный радиус.

Решено по ходу:

  • Угол вращения revolve_* — в градусах, directionType=dtNormal, toroidShapeType=сфероид, SetThinParam(false,…) для сплошного тела. Ось вращения — осевая линия (системный стиль 3).
  • Габариты — ksPart.GetGabarit(full=false) (только тела), координаты в мм.

Журнал решений по ходу

(дополняется автоматически во время работы)

⚠️ Конкурентность вызовов MCP и состояние сессии

MCP-сервер обрабатывает запросы конкурентно. Цикл моделирования с реестром эскизов по id (sketch_createsketch_add_*) предполагает, что клиент ждёт ответа на каждый вызов перед следующим (нормальное поведение LLM-клиентов). При «конвейерной» отправке зависимых вызовов возможна гонка (id ещё не зарегистрирован). STA-диспетчер сериализует COM, но не порядок постановки задач. На проработку: нужна ли серверная сериализация вызовов инструментов (очередь) для надёжности stateful-сессии? Пока полагаемся на последовательное поведение клиента.

Возврат изображения: ImageContentBlock.FromBytes

Поле ImageContentBlock.Data хранит base64-байты, а не сырые. Сырые байты PNG нужно передавать через ImageContentBlock.FromBytes(bytes, mimeType) — он сам кодирует. Прямое присваивание Data = rawBytes портит изображение на проводе. Исправлено в VisionTools.

МЦХ (get_part_info): API5 CalcMassInertiaProperties, а не API7

Сначала пробовали API7 IMassInertiaParam7 (от TopPart). Две проблемы:

  1. Единицы — отдаёт в единицах отображения МЦХ документа (по умолчанию см³/см²/г), зависит от настроек.
  2. Залипающий кэш — после первого расчёта свойство Actual остаётся TRUE и не сбрасывается при изменении геометрии через API5; Calculate() возвращает FALSE и не пересчитывает. Сценарий «запрос → достройка → запрос» отдавал устаревшее значение. Сброс через RebuildModel, переключение MassSettingMode — не помогли.

Решение ( в коде): считаем через API5 ksPart.CalcMassInertiaProperties(bitVector)ksMassInertiaParam (v/m/F/xc/yc/zc). Считает «по запросу» (без залипания), единицы задаются битовым вектором: ST_MIX_MM (0x1) | ST_MIX_KG (0x10)мм³, мм², кг, мм. Согласовано с тем, что деталь строится через API5 ksPart. Проверено двумя тестами: цилиндр R10×H20 (V≈6283 мм³, S≈1885 мм², m≈0.0494 кг, Zc=10 мм) и «запрос → штифт на грани → запрос» (виден корректный прирост ≈785 мм³). Материал по умолчанию — сталь (ρ≈7.856 г/см³).

Снимок 3D: рендер через файл, а не resultArrayBytes

ksDocument3D.SaveAsToRasterFormat(fileName, param) при непустом fileName возвращает TRUE, пишет корректный PNG на диск, но param.resultArrayBytes остаётся пустым. Поэтому SnapshotService рендерит во временный файл и читает байты обратно (надёжно проверено на v24 Home). На проработку: пустое имя файла + returnResultAsArrayBytes=true для чистого in-memory — не проверено; текущий путь через temp-файл работает.

STEP импорт/экспорт и list_components — реализованы

import_step, export_step (ConversionService, ConversionTools), list_components (QueryService.ListComponentsAsync) добавлены в v2+. Проверенные COM-приёмы:

  • Импорт: IApplication.get_Converter((object)(int)ksConverterFromSTEP=-3) (код формата, не путь к DLL) → IConverter.ConverterParameters(cmd)IAdditionConvertParameters.Format=ksConverterFromSTEPIKompasDocument3D1.ConvertFromAdditionFormat(path, prm).
  • Экспорт: ConvertToAdditionFormat с форматами AP203/AP214/AP242 (StepFormat enum в Core/Conversion/StepFormat.cs).
  • Param-коклассы (AdditionConvertParameters) — не CoCreatable, только через фабрики.
  • Обход сборки: IKompasDocument3D.TopPart → IPart7.Parts (IParts7).
  • Извлечение детали: prm.NeedCreateComponentsFiles=true + IPart7.OpenSourceDocument.

Прямое редактирование импортированной B-rep — move_face реализован

Решено. Перемещение грани продуктизировано как инструмент move_face / сервис FaceEditService (src/Kompas.Mcp.Core/Editing/FaceEditService.cs). Зарегистрирован в DI (Program.cs); интеграционный тест — Integration/FaceEditTests.cs.

Проверенный паттерн:

  • Объект грани берётся через IPart7.FindObjectsByPoint(x, y, z, true) → результат приводится к KompasAPI7.IFace. Не требует EntityCollection/SelectByPoint через API5.
  • Контейнеры операций не создаются отдельно: (ISurfaceContainer)part и (IModelContainer)part — COM-QI от IPart7 в рантайме.
  • FaceMover: SetFaces(faces) + Offset(distance) + Direction(true/false = наружу/внутрь) + Update().
  • Работает как на параметрической геометрии, так и на импортированной B-rep (проверено: деталь top_spacer, высота 39.45 → 41.45 мм).

Полный «рассечь→раздвинуть→объединить» (SplitSolids/BodyRepositions) — механика есть и драйвится, но отдельными MCP-инструментами не оформлена (возможная будущая опция).

Структурный осмотр модели — инспекция реализована

Решено. 7 инструментов (InspectionTools.cs, ModelInspectionService.cs, InspectionText.cs):

  • describe_model — структурный «паспорт» детали: габарит, МЦХ, тела, сводка топологии, дерево операций с параметрами, переменные. Предпочтителен перед model_snapshot.
  • list_features / list_bodies / list_variables — детальные срезы.
  • describe_face / describe_edge — drill-down по индексу.
  • measure — расстояние/угол между объектами (ksMeasurer).

Ключевые паттерны: дерево — ksPart.GetFeature()SubFeatureCollection(true,false); тела — BodyCollection()ksBody; переменные — VariableCollection()ksVariable. Детект «импорт без истории»: bodyCount>0 && нет формообразующих операций && features.Count<=bodyCount+1. Итог: 43 инструмента, 60 тестов.