Files
kompas3d-mcp/docs/OPEN_QUESTIONS.md
T
mikhail b73748f84b @
feat(query): инструмент get_part_info (МЦХ детали)

QueryService через IMassInertiaParam7: объём, масса, площадь, центр масс.
Единицы выставляются детерминированно (мм/кг) через LengthUnits/MassUnits —
иначе КОМПАС отдаёт значения в единицах отображения МЦХ (по умолчанию см/г).
Calculate() возвращает FALSE даже при успехе → опираемся на свойство Actual.

Интеграционный тест: цилиндр R10×H20 → V≈6283 мм³, S≈1885 мм², m≈0.049 кг, Zc=10 мм.
Находка задокументирована в OPEN_QUESTIONS; презентация обновлена (19 инструментов, 14 тестов).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@
2026-05-26 09:35:57 +03:00

7.9 KiB

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

Журнал спорных моментов и решений, принятых автономно (ночная сессия 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); добавить текст последней ошибки КОМПАС.

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

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

⚠️ Конкурентность вызовов 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): единицы измерения и поведение Calculate()

IMassInertiaParam7 по умолчанию возвращает значения в единицах отображения МЦХ документа (по умолчанию см³/см²/г, плотность г/см³) — то есть зависит от настроек, а не фиксированные СИ. Поэтому QueryService принудительно выставляет LengthUnits = ksLUnMM и MassUnits = ksMUnKG перед чтением → детерминированные мм³, мм², кг, мм. Проверено тестом (цилиндр R10×H20: V≈6283 мм³, S≈1885 мм², m≈0.0494 кг, Zc=10 мм). Тонкость: Calculate() возвращает FALSE даже при успехе (когда расчёт уже актуален после RebuildDocument), поэтому опираемся на свойство Actual, а не на возвращаемое значение. Материал по умолчанию — сталь (ρ≈7.856 г/см³); масса зависит от назначенного материала.

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

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