Files
mikhail 5d51b9e1af docs: выноска — TODO, README, ARCHITECTURE, OPEN_QUESTIONS, presentation, CLAUDE.md
Инкремент 9. 83 инструмента, 331 тест. + заметка о ревью реализации в спеке
(Codex + pi/glm-5.1 + pi/kimi-k2.6).

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

40 KiB
Raw Permalink 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 транзитов в построителе. Тот же паттерн у новых членов (SelectFaceByIndex, ShellAsync: транзитные ksShellDefinition/ FaceArray()/коллекции граней не освобождаются — сознательно, ради консистентности с Fillet/Chamfer; войдёт в общий рефакторинг 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. Итог: 62 инструмента, 101 тест.

hole — реализован через API7

Решено. Инструмент hole добавлен через API7 — в API5 интерфейс ksHoleDefinition отсутствует в interop (o3d_holeOperation=52 есть, но определения нет). Новый сервис HoleService (src/Kompas.Mcp.Core/Modeling/HoleService.cs) не является методом PartModeler.

Паттерн размещения: (IModelContainer)part7Points3D.Add() (создаёт IPoint3D как центр) → Holes3D.Add()IHole3D(IHoleDisposal)hole (BaseSurface = грань по FindObjectsByPoint, Perpendicular=true, AssociationVertex=point) → Update()RebuildDocument(). Эскиз размещения не нужен.

Ключевая тонкость — направление по объёму: Update() возвращает TRUE даже если сверлит «в воздух» снаружи тела (убыли материала нет). Решение: попытка Direction=true → Rebuild → сравниваем объём через API5 CalcMassInertiaProperties; если не убыл — пробуем Direction=false. Тот же класс проблем, что у boss/cut на грани (непредсказуемая ориентация нормали). При неуспехе обеих попыток — откат через IFeature7.Delete(). Добавлена валидация double.IsFinite (Infinity проходил мимо >0).

Отверстие не попадает в реестр _features API5 → возвращает подтверждение без id. +3 интеграционных теста (HoleTests.cs).

draft (уклон) — реализован через API5 Incline, а не API7

Решено. Ранее считалось, что уклон недоступен в API5 и требует API7. Это было заблуждением: поиск вёлся по слову "Draft", тогда как операция в API5 называется Incline (ksInclineDefinition, o3d_incline=42). Реализация — стандартный API5-паттерн PartModeler (как shell/rib/loft): NewEntity(o3d_incline)ksInclineDefinitionFaceArray() (грани по индексам) → SetPlane(координатная плоскость)angledirectionCreate() → регистрация в _features.

Ключевая тонкость (эмпирика): direction=false=расширение (outward), direction=true=сужение — обратно справке. Маппинг в коде: def.direction = !outward. Тот же класс проблем с направлением, что у boss/cut (v2-1). Валидация угла вынесена в SketchGeometry.RequireDraftAngle(0<angle<90). +9 unit-тестов + 2 integration-теста. Итог: 64 инструмента, 115 тестов (69 unit + 46 integration).

Примечание: o3d_DraftFromEdges=644 / IDraftFromEdges — это другая операция «уклон от базовой линии» (API7), здесь не используется.

Пакет D «параметрика» — CRUD переменных реализован

Решено (start). 3 новых инструмента: create_variable, set_variable, delete_variableVariableService (src/Kompas.Mcp.Core/Modeling/VariableService.cs) + VariableTools (src/Kompas.Mcp.Host/Tools/VariableTools.cs). Исправлен list_variables в ModelInspectionService.ReadVariables. Итог (на тот момент): 67 инструментов, 116 тестов (69 unit + 47 integration).

Ключевые API-тонкости (эмпирика):

  • Создание переменной — ТОЛЬКО через ksPart.GetFeature().VariableCollection (свойство-коллекция на корневом ksFeature). ksPart.VariableCollection() (метод) возвращает только внешние переменные и непригоден для создания пользовательских.
  • Expression — ведущее поле; Value — вычисленный результат. Изменяют значение через Expression.
  • После ksPart.RebuildModel() RCW ksVariable застревает на старом значении → перечитывать из свежей коллекции (ReadValueFresh). Зависимые переменные пересчитываются автоматически.
  • RemoveVariable возвращает FALSE при наличии зависимых переменных → удаление невозможно.
  • list_variables исправлен: ранее читал ksPart.VariableCollection() (только внешние), теперь ksPart.GetFeature().VariableCollection (все), с фолбэком на старый путь.

⚠️ Осознанное ограничение — геометрия не меняется: переменная управляет геометрией только в параметрической модели, где размеры эскизов привязаны к именам переменных. Наши эскизы строятся литеральными координатами — set_variable хранит и вычисляет значение, но геометрию НЕ меняет. Связь размеров эскизов с переменными (параметрические эскизы через API2D) — отдельный пласт, не реализован. Это осознанно задокументировано; реализация — продолжение пакета D.

Параметрические эскизы (пакет D продолжение) — исследовано, недоступно через COM-API

Исследовано и закрыто. ksCDimWithVariable (размер с переменной) не конструируется из внешней автоматизации — интерфейс недоступен в COM-клиентском режиме. Связать размеры эскиза с именем переменной через API2D невозможно без исполнения кода внутри процесса КОМПАС (plugin). Подробности — docs/superpowers/specs/2026-05-27-parametric-sketch-findings.md.

Отверстие с цековкой и зенковкой — реализовано

Решено. 2 новых инструмента: hole_counterbore (ksHTCounterbore, ISpotfacingHoleParameters) и hole_countersink (ksHTCountersinking, ICountersinkHoleParameters). HoleCore рефакторен под configure-колбэк. Итог: 69 инструментов, 118 тестов (69 unit + 49 integration).

Коническое отверстие — реализовано

Решено. Новый инструмент hole_conic — коническое (конусное) отверстие, сужающееся вглубь. Параметры: x,y,z, diameter (диаметр у основания), conicAngle (угол конуса, 0<angle<180), depth=0, throughAll=false. Реализован через тот же HoleCore-паттерн (configure-колбэк): HoleType=ksHTConic + configure устанавливает (IConicHoleParameters)hole.HoleParameters (ConicType=ksCNAngle, ConicAngle). Завершает набор типов отверстий: простое (hole), цековка (hole_counterbore), зенковка (hole_countersink), коническое (hole_conic). Итог: 70 инструментов, 119 тестов (69 unit + 50 integration).

Вставка компонента в сборку — реализована (начало класса «Сборки»)

Решено. Новый инструмент assembly_add_component(filePath, x, y, z) — первый инструмент, который пишет в сборку (ранее list_components только читал). Сервис AssemblyService (src/Kompas.Mcp.Core/Assemblies/), инструменты — AssemblyTools. Паттерн API7: IParts7.AddFromFileIPlacement3D.SetOriginUpdatePlacementRebuildModel(true). Ключевые тонкости: UpdatePlacement возвращает FALSE для вручную позиционируемого компонента — не ошибка; зафиксированный первый компонент двигается через SetOrigin+RebuildModel без разфиксации; RequireActiveAssembly проверяет тип документа до TopPart. Откат при неуспехе через IFeature7.Delete.

Сопряжения в сборках — реализованы (coincidence/distance)

Решено (инкремент 2). Новый инструмент assembly_add_mate(mateType, x1,y1,z1, x2,y2,z2, value=0). Паттерн API7: top.MateConstraints (IMateConstraints3D) → Add(MateConstraintType)BaseObject1/2 (грани через top.FindObjectsByPoint(x,y,z,FirstLevel=false)) → ParamValueUpdate()RebuildModel(true); проверка mate.Valid. Типы mc_Coincidence=0/mc_Distance=5 проверены вживую. Enum MateType + static Mates в src/Kompas.Mcp.Core/Assemblies/MateType.cs. Итог: 72 инструмента, 156 тестов (98 unit + 58 integration). Следующий шаг: прочие типы сопряжений (parallel, perpendicular, concentric, angle, tangency).

Стандартные виды чертежа — реализованы (начало вехи «2D-чертёж»)

Решено. Новый инструмент drawing_create_standard_views(partFilePath, scale, x, y) — создаёт ассоциативные виды (спереди/сверху/слева) на активном чертеже. Реализовано через API7 DrawingService (namespace Kompas.Mcp.Core.Drawings), DrawingTools. Паттерн: IKompasDocument2D.ViewsAndLayersManager.Views.AddStandartViews(path, "#Спереди", object[]{1,3,5}, x, y, scale, 20, 20)object[] как SAFEARRAY VT_I4. Проверено вживую: Views.Count 1→4, ObjectCount>0 сразу (rebuild не нужен).

Основная надпись (штамп) чертежа — реализована (инкремент 2 вехи «2D-чертёж»)

Решено. Новый инструмент drawing_fill_title_block(designation?, name?, material?) — заполняет графы основной надписи активного чертежа. Паттерн API7: doc2d.LayoutSheets.ItemByNumber[1].Stamp (IStamp) → stamp.Text[columnId].Str = textstamp.Update(). IText.Str замещает содержимое напрямую (повторная запись перезаписывает — Clear не нужен, подтверждено round-trip тестом). Номера граф ksStampEnum: обозначение=2, наименование=1, материал=3. Свой enum StampField + StampFields.ColumnId/Collect в src/Kompas.Mcp.Core/Drawings/StampField.cs; инструмент в DrawingTools.cs. Итог: 74 инструмента, 188 тестов (123 unit + 65 integration). Следующий шаг: размеры на видах.

Линейные размеры на видах чертежа — реализованы (инкремент 3 вехи «2D-чертёж»)

Решено. Новый инструмент drawing_add_linear_dimension(x1,y1,x2,y2,x3,y3, orientation, viewNumber) — ставит линейный размер на виде активного чертежа. Паттерн API7: получить вид по viewNumber → COM-QI (ISymbols2DContainer)view (размер адресуется конкретному виду, а не листу) → LineDimensions.Add()ILineDimensionX1,Y1,X2,Y2 (выносные точки в локальной СК вида, мм), X3,Y3 (положение размерной линии), Orientation (ksLineDimensionOrientationEnum: ksLinDParallel=0/ksLinDHorizontal=1/ksLinDVertical=2), AutoNominalValue=trueUpdate()Valid → значение ((IDimensionText)dim).NominalValue. Откат dim.Delete() при ошибке или нулевом значении. Ключевая находка: размеры НЕ входят в IView.ObjectCount (тот считает геометрию IDrawingContainer); размеры живут в ISymbols2DContainer отдельно — попадание размера в вид проверяется через ISymbols2DContainer.LineDimensions.Count. DrawingViewsResult расширен полем ViewNumbers (номера созданных видов — для адресации при простановке размеров). Свой enum DimensionOrientation {Horizontal, Vertical, Parallel} + DimensionOrientations.Parse/ToKompas. Валидаторы DrawingValidation.RequireFiniteCoords + RequireDistinctPoints. Новые файлы: DimensionOrientation.cs, DrawingDimensionResult.cs; инструмент в DrawingTools.cs. Итог: 75 инструментов, 203 теста (131 unit + 72 integration). Следующий шаг: диаметральные/радиальные/угловые размеры, рамка/формат.

Диаметральный размер на виде чертежа — реализован (инкремент 4 вехи «2D-чертёж»)

Решено. Новый инструмент drawing_add_diametral_dimension(xc,yc,radius, angle, viewNumber) — ставит диаметральный размер (Ø) на виде активного чертежа. Паттерн API7: общий хелпер RequireSymbols2DContainer(viewNumber) (переиспользуется с линейным путём) → DiametralDimensions.Add()IDiametralDimension (Xc, Yc, Radius в локальной СК вида, Angle в радианах через DimensionAngles.ToRadians, AutoNominalValue=true) → Update()Valid((IDimensionText)dim).NominalValue = диаметр (2·Radius). Размер «свободный» (без BaseObject — ассоциативная привязка отложена). Валидатор DrawingValidation.RequirePositiveRadius. DimensionAngles.ToRadians (с проверкой переполнения) добавлен в DimensionOrientation.cs; RequirePositiveRadius — в DrawingValidation.cs; инструмент в DrawingTools.cs. Итог: 76 инструментов, 218 тестов (141 unit + 77 integration).

Радиальный и угловой размеры на виде чертежа — реализованы (инкремент 5 вехи «2D-чертёж»)

Решено. Два новых инструмента завершают базовое семейство размеров:

drawing_add_radial_dimension(xc,yc,radius, angle=0, viewNumber) — радиальный размер (R). Паттерн API7: RequireSymbols2DContainer(viewNumber)RadialDimensions.Add()IRadialDimension (Xc, Yc, Radius, Angle в радианах, DimensionType=true, AutoNominalValue=true) → Update()ValidNominalValue = радиус. Переиспользует RequirePositiveRadius и DimensionAngles.ToRadians.

drawing_add_angular_dimension(xc,yc, x1,y1, x2,y2, x3,y3, angleType="min", viewNumber) — угловой размер. angleType = "min" (острый) | "max" (тупой) | "more" (рефлексный >180°). Паттерн API7: RequireSymbols2DContainer(viewNumber)AngleDimensions.Add(ksDrADimension)IAngleDimension (Xc,Yc,X1,Y1,X2,Y2,X3,Y3, DimensionType=ksAngleDimTypeEnum, AutoNominalValue=true) → Update()ValidNominalValue в градусах. Новый enum AngleDimensionType {Min,Max,More} + AngleDimensionTypes.Parse/ToKompas в DimensionOrientation.cs (+9 unit-тестов для маппинга).

Итог: 78 инструментов, 241 тест (150 unit + 91 integration). Базовое семейство размеров завершено — виды, штамп, размеры (линейный, диаметральный, радиальный, угловой). Нереализовано: текстовые обозначения (шероховатость, допуски формы, выноски, тех. требования), ассоциативная привязка, рамка/формат.

Текстовые обозначения на чертеже — реализованы (инкремент 6 вехи «2D-чертёж»)

Решено. Три новых инструмента завершают класс «2D-чертёж» на уровне текстовых аннотаций:

drawing_add_rough(x,y, value?, signType, angle=0, viewNumber) — знак шероховатости на виде. Паттерн API7: ISymbols2DContainer.Roughs.Add()IRough (BranchX0/Y0/Angle) → (IRoughParams)rough (SignType=ksRoughSignEnum, RoughParamText.Str = value) → Update()Valid. Новый enum RoughSignType {NoProcessing, DeleteMaterial, WithoutDeleteMaterial} + RoughSignTypes.Parse/ToKompas в Drawings/RoughSignType.cs. Возвращает DrawingAnnotationResult { Value, ViewNumber }.

drawing_add_text(x,y, text, angle=0, viewNumber) — свободная текстовая надпись на виде. Паттерн API7: (IDrawingContainer)view.DrawingTexts.Add()IDrawingText (X/Y/Angle) → (IText)dt.Str = textUpdate()Valid. ВАЖНО: текст живёт в IDrawingContainer, а не ISymbols2DContainer; проверяется через DrawingTexts.Count. Возвращает DrawingAnnotationResult { Value, ViewNumber }.

drawing_set_technical_requirements(text) — технические требования документа (уровень документа, не вида). Паттерн API7: (IDrawingDocument)doc.TechnicalDemandtd.Text.Str = texttd.Update(). Перезаписывает прежние. Возвращает число строк.

Итог: 81 инструмент, 267 тестов (163 unit + 104 integration). Нереализовано: ассоциативная привязка размеров и шероховатости к геометрии (BaseObject), рамка/формат листа, выноски (Leaders), обозначения баз (Bases), допуски формы (Tolerances).

Ассоциативная привязка диаметрального/радиального к окружности — реализована (инкремент 7 вехи «2D-чертёж»)

Решено (частично). Параметр associate=true добавлен к drawing_add_diametral_dimension и drawing_add_radial_dimension. Реализация (API7, DrawingService): новый класс CircularObjectMatch — отбирает окружность-кандидата в IDrawingContainer.Circles по центру (xc,yc) + радиусу с допуском 1 мм; бросает при отсутствии или неоднозначности (концентрические окружности различаются по радиусу). Хелпер RequireViewContainers получает одновременно ISymbols2DContainer и IDrawingContainer для вида. _Circle реализует IDrawingObject, поэтому dim.BaseObject = circle без дополнительного приведения. associate=false — прежнее «свободное» поведение без изменений. Подтверждено спайком: BaseObject=circle без задания Xc/Yc/Radius → Valid, NominalValue из геометрии (цилиндр R10: Ø=20 / R=10). +5 unit-тестов (CircularObjectMatchTests) + 6 интеграционных (DrawingAssociativeDimensionTests). Итог: 81 инструмент, 278 тестов (168 unit + 110 integration).

Нереализовано (следующие инкременты): привязка к дуге (IDrawingContainer.Arcs), ассоциативный угловой размер (BaseObject1/2, отрезки), ассоциативная шероховатость (IRough.BaseObject); рамка/основная надпись по ГОСТ-стилю (LayoutLibraryFileName/LayoutStyleNumber), несколько листов; выноски (Leaders), обозначения баз (Bases), допуски формы (Tolerances).

Формат и ориентация листа чертежа — реализованы (инкремент 8 вехи «2D-чертёж»)

Решено. Новый инструмент drawing_set_sheet_format(format, landscape, width?, height?, sheetNumber=1) — задаёт формат и ориентацию активного листа чертежа. Паттерн API7: doc2d.LayoutSheets.ItemByNumber[sheetNumber]ILayoutSheet.Format (ISheetFormat: Format=ksDocumentFormatEnum, VerticalOrientation=!landscape, FormatWidth/Height только для format=user) → sheet.Update(). Дефолтный лист нового чертежа — A4 книжный. Новые типы: enum PaperFormat {A0,A1,A2,A3,A4,A5,User} + PaperFormats.Parse/ToKompas/FromKompas; record SheetFormatResult; валидатор DrawingValidation.ValidateFormatDimensions; хелпер RequireLayoutSheet. Ключевая тонкость: для format=user КОМПАС выводит VerticalOrientation из соотношения сторон (height>width), флаг landscape в этом случае игнорируется — результат читается read-back из COM. Для стандартных форматов width/height запрещены (должны быть 0). +20 unit-тестов (PaperFormats.Parse, ValidateFormatDimensions) + 10 интеграционных (DrawingSheetFormatTests). Итог: 82 инструмента, 308 тестов (188 unit + 120 integration).

Нереализовано: рамка по ГОСТ-стилю (LayoutLibraryFileName/LayoutStyleNumber), несколько листов.

Линия-выноска с текстом — реализована (инкремент 9 вехи «2D-чертёж»)

Решено. Новый инструмент drawing_add_leader(x,y, textX,textY, text, shelfDirection, viewNumber) — ставит линию-выноску с надписью на полке на виде активного чертежа. Паттерн API7: ISymbols2DContainer.Leaders.Add(ksDrLeader)IBaseLeader(IBranchs)bl.AddBranchByPoint(0,x,y) (остриё, ответвление ответвлений — обязательно до Update, иначе RPC_E_SERVERFAULT) → SetBranchTextPosition(textX,textY)(ILeader)bl.TextOnShelf.Str = text → опционально ShelfDirectionUpdate()Valid. Новый enum ShelfDirection {Auto,Right,Left,Up,Down} + ShelfDirections.Parse/ToKompas в src/Kompas.Mcp.Core/Drawings/ShelfDirection.cs; переиспользует DrawingAnnotationResult { Value, ViewNumber }. +13 unit-тестов (ShelfDirections.Parse) + 10 интеграционных (DrawingLeaderTests, включая параметризацию направлений полки). Итог: 83 инструмента, 331 тест (201 unit + 130 integration).

Нереализовано: обозначения баз (Bases), допуски формы (Tolerances); привязка к дуге (Arcs), ассоциативный угловой размер, ассоциативная шероховатость (IRough.BaseObject); рамка по ГОСТ-стилю; специальные выноски (позиция, клеймо, маркер).