Files
kompas3d-mcp/docs/OPEN_QUESTIONS.md
T
mikhail 8c560fd0f6 @
docs: ревью v2 в OPEN_QUESTIONS (направление операций, индексы граней, утечки RCW)

Зафиксированы наблюдения по новому слою v2 и отложенные на проработку пункты
(v2-1 направление boss/cut на грани зависит от нормали; v2-2 транзитные RCW в
PartModeler; v2-3 стабильность индексов граней; v2-4 fillet/chamfer по одному ребру),
а также принятые по ходу решения (углы вращения, габариты).

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

115 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Открытые вопросы для проработки
Журнал спорных моментов и решений, принятых автономно (ночная сессия 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`); добавить текст последней ошибки КОМПАС.
### Ревью 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_faces``sketch_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_create``sketch_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-файл работает.