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> @
7.9 KiB
Открытые вопросы для проработки
Журнал спорных моментов и решений, принятых автономно (ночная сессия 2026-05-25/26). Помечено: ⚠️ = требует вашего решения; ✅ = решено по умолчанию, можно пересмотреть.
⚠️ 1. API5 vs API7 для построения 3D
Контекст. Примеры SDK строят 3D через API5 (ksPart.NewEntity(o3d_*) →
ksBossExtrusionDefinition → Create()). API7 предлагает IPart7 → IModelContainer →
IExtrusions.Add().
Решение по умолчанию (✅ в коде): в v1 строю 3D через API5 ksPart (надёжно, повторяет
рабочие примеры Step3d1). API7 использую для приложения/документов/версии.
На проработку: переводить ли операции на чистый API7 позже (плюс — единообразие, минус — риск).
✅ 2. Точка входа подключения — API5
Для построения 3D нужен KompasObject (API5), а для снимка — ksDocument3D (API5).
Поэтому KompasSession подключается через KOMPAS.Application.5 → KompasObject, затем
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_create → sketch_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-файл работает.