chore: initial skeleton + KOMPAS connection spike (S1/S2)
Зафиксирован фундамент проекта MCP-сервера КОМПАС-3D: - docs/: ARCHITECTURE.md, IMPLEMENTATION_PLAN.md, presentation.html - tools/kdoc.py: навигатор по справке SDK - libs/kompas-interop/: вендорские interop-сборки КОМПАС из SDK - src/Kompas.Mcp.ConnectSpike: net8.0-windows консоль, доказывает работу COM-интеропа и attach/launch подключения к КОМПАС (IApplication, версия 24.0) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+64
@@ -0,0 +1,64 @@
|
|||||||
|
# ============================================================================
|
||||||
|
# .gitignore — KompasMcp (MCP-сервер КОМПАС-3D, .NET/C#)
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# ---- .NET / MSBuild build output -------------------------------------------
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
artifacts/
|
||||||
|
*.binlog
|
||||||
|
project.lock.json
|
||||||
|
project.assets.json
|
||||||
|
*.nuget.props
|
||||||
|
*.nuget.targets
|
||||||
|
|
||||||
|
# ---- Visual Studio / Rider / VS Code local state ---------------------------
|
||||||
|
.vs/
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.user
|
||||||
|
*.suo
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# ---- Test / coverage -------------------------------------------------------
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Cc]overage*/
|
||||||
|
*.coverage
|
||||||
|
*.trx
|
||||||
|
|
||||||
|
# ---- NuGet -----------------------------------------------------------------
|
||||||
|
*.nupkg
|
||||||
|
*.snupkg
|
||||||
|
**/packages/*
|
||||||
|
!**/packages/build/
|
||||||
|
|
||||||
|
# ---- Claude Code local settings (не разделяемые) ---------------------------
|
||||||
|
.claude/settings.local.json
|
||||||
|
|
||||||
|
# ---- Python tooling (tools/) -----------------------------------------------
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
|
||||||
|
# ---- OS-мусор --------------------------------------------------------------
|
||||||
|
Thumbs.db
|
||||||
|
ehthumbs.db
|
||||||
|
Desktop.ini
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Большие вендорские/генерируемые артефакты — НЕ коммитим
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# Офлайн-зеркало справки SDK КОМПАС-3D (~1.4 ГБ, 26k+ файлов, © АСКОН).
|
||||||
|
# Лежит локально; для навигации см. tools/kdoc.py. Скачивается отдельно.
|
||||||
|
docs/KOMPAS_SDK_ru-RU/
|
||||||
|
|
||||||
|
# Индекс справки, генерируется из зеркала командой: python tools/kdoc.py build-index
|
||||||
|
docs/kompas_sdk_index.tsv
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Отслеживается НАМЕРЕННО (нужно для сборки), не игнорировать:
|
||||||
|
# libs/kompas-interop/*.dll — interop-сборки КОМПАС из SDK (Samples/Common)
|
||||||
|
# ============================================================================
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## Project goal
|
||||||
|
|
||||||
|
Build an **MCP (Model Context Protocol) server** that lets an LLM drive the CAD system **КОМПАС-3D** (ASCON). The server exposes КОМПАС operations (create/open documents, build 2D geometry, 3D parts and assemblies, read/write parameters, run macros) as MCP tools.
|
||||||
|
|
||||||
|
Authoritative references:
|
||||||
|
- **Offline SDK docs mirror in this repo: `docs/KOMPAS_SDK_ru-RU/`** — navigate it with `tools/kdoc.py` (see below). Prefer this over the web.
|
||||||
|
- Online SDK docs: https://help.ascon.ru/KOMPAS_SDK/22/ru-RU/index.html
|
||||||
|
- Local SDK install: `C:\Program Files\ASCON\KOMPAS-3D v24 Home\SDK`
|
||||||
|
|
||||||
|
## Navigating the SDK docs (`tools/kdoc.py`)
|
||||||
|
|
||||||
|
`docs/KOMPAS_SDK_ru-RU/` is a 26 000-page Help&Manual WebHelp export — raw HTML buried under header/footer/JS noise, with cryptic filenames. **Do not browse it by hand.** Use the helper:
|
||||||
|
|
||||||
|
```
|
||||||
|
python tools/kdoc.py search "<russian or interface keywords>" # find pages: prints "file.html\tTitle"
|
||||||
|
python tools/kdoc.py read <file.html> # print clean topic text (no markup)
|
||||||
|
python tools/kdoc.py build-index # regenerate the index (only if docs change)
|
||||||
|
```
|
||||||
|
|
||||||
|
`search` does AND-matching over titles + excerpts; `read` extracts just the topic body (breadcrumb path, data type, Automation/COM syntax, parameters, return value, notes). Example: `search Visible IApplication` → `iapplication_visible.html`, then `read iapplication_visible.html`.
|
||||||
|
|
||||||
|
The index lives at `docs/kompas_sdk_index.tsv` (`filename<TAB>title<TAB>excerpt`) and can also be searched with the Grep tool directly — handy for fanning out over many interface names at once.
|
||||||
|
|
||||||
|
**The docs contain two parallel mirrors of the same interfaces:**
|
||||||
|
- *«API интерфейсов. Версия 7»* — filenames **without** prefix (`iapplication_*`, `idocuments_*`). This is the **COM API7/API5** reference — the one this project uses.
|
||||||
|
- *«API интерфейсов. KsAPI»* — `ksapi_*`-prefixed filenames: the cross-platform Qt/C++ **KsAPI** flavour. Same concepts, different (non-COM) binding — ignore unless explicitly working with KsAPI.
|
||||||
|
|
||||||
|
## Current state
|
||||||
|
|
||||||
|
The repository is **empty / greenfield** — no source, no git, no build system yet. The implementation language and MCP transport have **not** been chosen. Before writing code, decide and record (here) the stack. The natural fit is **.NET / C#** because the КОМПАС API ships as COM type libraries with first-class C# Interop samples (see SDK `Samples/CSharp.zip`), but a Python MCP server using `pywin32`/`comtypes` is also viable.
|
||||||
|
|
||||||
|
## КОМПАС-3D API architecture (the critical context)
|
||||||
|
|
||||||
|
КОМПАС-3D is automated via a **COM Automation API**, Windows-only. КОМПАС-3D **must be installed and running/launchable** on the same machine — the MCP server is a COM client, not a standalone CAD engine. There are **two coexisting API generations**:
|
||||||
|
|
||||||
|
| | Entry point | Namespace (C#) | Style | Use when |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| **API5** (legacy) | `KompasObject` | `Kompas6API5` | procedural, flat | older features, 2D primitives, bootstrapping |
|
||||||
|
| **API7** (modern, preferred) | `IApplication` / `_Application` | `KompasAPI7` | OOP, interface-based | everything new — documents, 3D, parameters |
|
||||||
|
|
||||||
|
Constants live in separate libraries: `Kompas6Constants`, `Kompas6Constants3D` (C#), or `ksConstants.h` / `ksConstants3D.h` (C++).
|
||||||
|
|
||||||
|
### Connection pattern (verified from `Samples/CSharp.zip`)
|
||||||
|
|
||||||
|
1. Get the КОМПАС root object by COM ProgID. **Verified on this machine (v24 Home):**
|
||||||
|
`KOMPAS.Application.7` and `KOMPAS.Application.5` are registered; `KOMPASLT.*` is **absent** —
|
||||||
|
the Home edition uses the regular (non-LT) ProgIDs. Resolution order:
|
||||||
|
- `KOMPAS.Application.7` — direct API7 `IApplication` (preferred)
|
||||||
|
- `KOMPAS.Application.5` — API5 `KompasObject`, then `ksGetApplication7()`
|
||||||
|
- `KOMPASLT.Application.5` — fallback for other installations
|
||||||
|
- New instance: `Activator.CreateInstance(Type.GetTypeFromProgID(progId))`
|
||||||
|
- Attach to a running instance: `Marshal.GetActiveObject(progId)`
|
||||||
|
2. If you entered via API5, obtain the modern application: `IApplication appl = (IApplication)kompas.ksGetApplication7();` (via `KOMPAS.Application.7` you already hold `IApplication`).
|
||||||
|
3. Set `appl.Visible = true` to show the КОМПАС window; drive documents via API7 interfaces (`IKompasDocument2D`, `IKompasDocument3D`, …).
|
||||||
|
|
||||||
|
> The SDK C# samples are mostly **КОМПАС plugin libraries** (DLLs loaded *into* КОМПАС as ActiveX, entry methods like `ExternalRunCommand`/`ExternalMenuItem`, registered via `[ComRegisterFunction]`). For an MCP server we want the **opposite direction**: a standalone *external automation client* that connects out-of-process via the ProgID pattern above. Read the samples for API usage, not for the plugin packaging/registration boilerplate.
|
||||||
|
|
||||||
|
## SDK layout (read-only reference, not part of this repo)
|
||||||
|
|
||||||
|
Under `C:\Program Files\ASCON\KOMPAS-3D v24 Home\SDK`:
|
||||||
|
- `lib\*.tlb` — COM type libraries to reference / generate interop from: `kAPI5.tlb`, `kAPI7.tlb`, `ksConstants.tlb`, `ksConstants3D.tlb`, `kAPI2D5COM.tlb`, `kAPI3D5COM.tlb`.
|
||||||
|
- `lib64\` — 64-bit import libs (`kAPI7.lib`, `kAPI5.lib`, …) and `.a` for C++/Builder.
|
||||||
|
- `Include\` — C++/Pascal headers (`Ks_TLB.h`, `kAPI2D5COM.h`, `LDefin2D.pas`, …).
|
||||||
|
- `KsAPI\` — the **KsAPI** (Qt/C++ cross-platform flavour): `Include\KsAPI.h`, `ksConstants*.h`, `Lib64\ksAPI.lib`, plus `Help\С чего начать.pdf` and a Linux/Debian 12 build guide.
|
||||||
|
- `Samples\` — zipped examples per language: `CSharp.zip`, `C++.zip`, `Pascal.zip`, `Basic.zip`. Inside `CSharp.zip`, the `Step1`…`Step12` and `Step2_API7_2D` / `Step2_API7_3D` projects are the progressive tutorial; `Gayka`, `SlideWrk`, `EventsAuto` are larger feature demos.
|
||||||
|
- `Help\KOMPAS_SDK_ru-RU.zip` and `KsAPI\Help\KsAPI_Help.zip` — offline copy of the docs.
|
||||||
|
|
||||||
|
To inspect a sample without unpacking the whole archive: `unzip -p "<path>\Samples\CSharp.zip" "Automation/Step2_API7_3D/Step2_API7_3D.cs"`. Note the `.cs` sample sources are **Windows-1251 encoded** (Cyrillic comments appear garbled in UTF-8 tools).
|
||||||
|
|
||||||
|
## Conventions / gotchas
|
||||||
|
|
||||||
|
- **Windows-only & 64-bit**: target x64 to match the installed КОМПАС; mixing bitness breaks COM activation. КОМПАС v24 Home is the installed edition — some full-edition API features may be unavailable.
|
||||||
|
- **Encoding**: SDK source samples are CP1251. New project files should be UTF-8.
|
||||||
|
- **COM lifetime**: release COM objects (`Marshal.ReleaseComObject`) / scope them; a leaked reference keeps the КОМПАС process alive.
|
||||||
|
- **API choice**: prefer API7 for new tool implementations; drop to API5 only for things API7 doesn't expose.
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<Project>
|
||||||
|
<!-- Общие настройки для всех проектов решения KompasMcp. -->
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0-windows</TargetFramework>
|
||||||
|
<Platforms>x64</Platforms>
|
||||||
|
<!-- COM-активация 64-битного КОМПАС требует x64-процесса. -->
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||||
|
<!-- Интероп-сборки КОМПАС лежат в libs/kompas-interop. -->
|
||||||
|
<KompasInteropDir>$(MSBuildThisFileDirectory)libs\kompas-interop</KompasInteropDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<Solution>
|
||||||
|
<Folder Name="/src/">
|
||||||
|
<Project Path="src/Kompas.Mcp.ConnectSpike/Kompas.Mcp.ConnectSpike.csproj" />
|
||||||
|
</Folder>
|
||||||
|
</Solution>
|
||||||
@@ -0,0 +1,305 @@
|
|||||||
|
# Архитектура MCP-сервера КОМПАС-3D (предлагаемый вариант)
|
||||||
|
|
||||||
|
> Статус: **черновик / предлагаемый вариант**, открыт к правкам.
|
||||||
|
> Назначение: зафиксировать выбор стека и целевую архитектуру решения до начала
|
||||||
|
> реализации. Сопутствующий контекст по COM API КОМПАС — в [`../CLAUDE.md`](../CLAUDE.md).
|
||||||
|
> Имена интерфейсов и перечислений сверены со справкой SDK через `tools/kdoc.py`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Назначение и контекст
|
||||||
|
|
||||||
|
Строим **MCP-сервер**, который даёт LLM управлять CAD-системой **КОМПАС-3D** (АСКОН):
|
||||||
|
создавать/открывать документы, строить 2D-геометрию, 3D-детали и сборки, читать/писать
|
||||||
|
параметры, запускать команды.
|
||||||
|
|
||||||
|
Жёсткие рамки, заданные природой API:
|
||||||
|
|
||||||
|
- **Только Windows, только x64** — КОМПАС автоматизируется через **COM Automation API**;
|
||||||
|
разрядность сервера должна совпадать с разрядностью установленного КОМПАС.
|
||||||
|
- **КОМПАС должен быть установлен и запускаем на той же машине.** Сервер — это **COM-клиент**,
|
||||||
|
а не самостоятельный геометрический движок. Без работающего экземпляра КОМПАС инструменты
|
||||||
|
бесполезны.
|
||||||
|
- Целевая среда — **КОМПАС-3D v24 Home** (LT-редакция); часть возможностей полной редакции
|
||||||
|
может быть недоступна.
|
||||||
|
|
||||||
|
Две сосуществующие версии API (подробнее — `CLAUDE.md`):
|
||||||
|
|
||||||
|
| | Точка входа | Namespace (C#) | Когда |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **API5** (legacy) | `KompasObject` | `Kompas6API5` | bootstrap-подключение, построение 3D через `ksPart` |
|
||||||
|
| **API7** (современный) | `IApplication` | `KompasAPI7` | всё новое: документы, параметры, операции |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Выбранный стек
|
||||||
|
|
||||||
|
| Решение | Выбор | Обоснование |
|
||||||
|
|---|---|---|
|
||||||
|
| Язык / платформа | **.NET 8 (LTS), C#, `win-x64`** | COM-интероп первого класса; SDK поставляет именно C#-примеры и `.tlb` |
|
||||||
|
| MCP SDK | **`ModelContextProtocol`** (официальный C# SDK) | хостинг через `Microsoft.Extensions.Hosting`, DI, готовый stdio-транспорт |
|
||||||
|
| Транспорт | **stdio** | локальный клиент (Claude Desktop / IDE) запускает сервер дочерним процессом на машине с КОМПАС — проще и безопаснее всего |
|
||||||
|
| COM-интероп | COM-references на `.tlb` из `SDK\lib` | строгая типизация интерфейсов API7 (`IApplication`, `IPart7`, …) на этапе компиляции |
|
||||||
|
| Логи | `Microsoft.Extensions.Logging` → **stderr** | **stdout занят протоколом MCP** — туда писать нельзя |
|
||||||
|
| Сериализация | `System.Text.Json` | штатно в .NET, без зависимостей |
|
||||||
|
|
||||||
|
Подключаемые типобиблиотеки (`C:\Program Files\ASCON\KOMPAS-3D v24 Home\SDK\lib`):
|
||||||
|
`kAPI5.tlb`, `kAPI7.tlb`, `ksConstants.tlb`, `ksConstants3D.tlb`, `kAPI2D5COM.tlb`, `kAPI3D5COM.tlb`.
|
||||||
|
Namespaces: `Kompas6API5`, `KompasAPI7`, `Kompas6Constants`, `Kompas6Constants3D`, `KAPITypes`.
|
||||||
|
|
||||||
|
### Почему C#, а не Python
|
||||||
|
|
||||||
|
Python (comtypes/pywin32) технически способен управлять COM, но КОМПАС-API активно использует
|
||||||
|
конструкции, болезненные для динамического интеропа: `out`/`ref`-параметры (например,
|
||||||
|
`ExecuteKompasCommand(commandID, post, &retval)`), `SAFEARRAY`/динамические массивы
|
||||||
|
(`ksDynamicArray`, `ksLtVariant`), мосты API5↔API7 (`ksGetApplication7`, `TransferInterface`) и
|
||||||
|
обязательный STA + насос сообщений. В C# всё это покрыто строго типизированным interop, что
|
||||||
|
снижает риск и стоимость поддержки.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Модель процесса и потоков (ключевой раздел)
|
||||||
|
|
||||||
|
COM-объекты КОМПАС живут в **STA (Single-Threaded Apartment)**, экземпляр приложения —
|
||||||
|
**единственный** и **однопоточный**. Отсюда — центральное архитектурное ограничение:
|
||||||
|
|
||||||
|
- Сервер держит **один выделенный STA-поток**, который **владеет всеми без исключения
|
||||||
|
COM-вызовами**. Асинхронные обработчики MCP-инструментов (поток-пул) **маршалят** работу в этот
|
||||||
|
поток через очередь/диспетчер (например, `BlockingCollection<Func<…>>` или собственный
|
||||||
|
`SynchronizationContext`). Это автоматически **сериализует** доступ к КОМПАС.
|
||||||
|
- STA-поток крутит **насос сообщений Windows** и периодически вызывает
|
||||||
|
`IApplication.PumpWaitingMessages()` / при необходимости `EnableTaskAccess(false)` на время
|
||||||
|
пакетных операций — чтобы диалоги и колбэки КОМПАС не приводили к взаимоблокировке.
|
||||||
|
|
||||||
|
```
|
||||||
|
MCP client ──stdio──> Host (поток-пул, async)
|
||||||
|
│ ставит задачу в очередь
|
||||||
|
▼
|
||||||
|
┌─────────────────────────┐
|
||||||
|
│ STA-поток (COM owner) │ ← насос сообщений + PumpWaitingMessages
|
||||||
|
│ KompasObject / IApplication
|
||||||
|
└─────────────────────────┘
|
||||||
|
│ COM
|
||||||
|
▼
|
||||||
|
КОМПАС-3D.exe
|
||||||
|
```
|
||||||
|
|
||||||
|
### Подключение к КОМПАС
|
||||||
|
|
||||||
|
1. Разрешить ProgID. На целевой машине (v24 Home) **проверено**: зарегистрированы
|
||||||
|
`KOMPAS.Application.7` и `KOMPAS.Application.5`, а `KOMPASLT.*` **отсутствует** — то есть
|
||||||
|
Home-редакция использует обычные ProgID, а не LT. Порядок попыток:
|
||||||
|
`KOMPAS.Application.7` (прямой API7) → `KOMPAS.Application.5` (+ `ksGetApplication7()`) →
|
||||||
|
`KOMPASLT.Application.5` (запасной, для других инсталляций). Выбранный ProgID фиксировать в логе.
|
||||||
|
2. **Attach** к запущенному экземпляру: `Marshal.GetActiveObject(progId)`.
|
||||||
|
Если не запущен — **launch**: `Activator.CreateInstance(Type.GetTypeFromProgID(progId))`.
|
||||||
|
3. Если зашли через API5 — получить современное приложение:
|
||||||
|
`IApplication appl = (IApplication)kompas.ksGetApplication7();` (через `KOMPAS.Application.7` `IApplication` уже на руках).
|
||||||
|
4. `appl.Visible = true;` — показать окно КОМПАС; дальше работать через интерфейсы API7.
|
||||||
|
|
||||||
|
### Жизненный цикл COM
|
||||||
|
|
||||||
|
- Корневые RCW (`KompasObject`, `IApplication`) держим на всё время жизни сервера.
|
||||||
|
- Временные объекты освобождаем (`Marshal.ReleaseComObject`) в обратном порядке создания.
|
||||||
|
- `appl.Quit()` вызываем **только если экземпляр запустили мы сами** (флаг «launched-by-us»);
|
||||||
|
к чужому запущенному КОМПАС не применяем.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Слои решения
|
||||||
|
|
||||||
|
В v1 — логические слои (папки/проекты одного solution `KompasMcp.sln`):
|
||||||
|
|
||||||
|
| Слой | Ответственность |
|
||||||
|
|---|---|
|
||||||
|
| **Kompas.Mcp.Host** | Точка входа. Сборка MCP-сервера (stdio), DI, логи в stderr, старт и владение STA-потоком, graceful shutdown. |
|
||||||
|
| **Kompas.Mcp.Tools** | Определения MCP-инструментов по категориям (System / Documents / Sketch / Features / Query). **Тонкий слой**: валидация аргументов → постановка задачи на STA-поток → форматирование результата/ошибки в MCP-ответ. |
|
||||||
|
| **Kompas.Mcp.Core** (`KompasSession`) | COM-слой. Менеджер соединения, менеджер документов, построитель эскизов, построитель операций, помощники выборки граней/плоскостей. Безопасные типизированные методы, null-проверки, трансляция ошибок. Здесь живут все паттерны работы с API5/API7. |
|
||||||
|
| **Kompas.Mcp.Interop** | Interop-сборки из `.tlb` + маппинг «дружелюбная строка ↔ перечисление» (`DocumentTypeEnum`, `Obj3dType`, `ksEndTypeEnum`, `ksDirectionTypeEnum`). |
|
||||||
|
|
||||||
|
Принцип: **только `Core` и `Interop` знают про COM**. `Tools` оперируют доменными
|
||||||
|
DTO и вызывают `Core`; `Host` не содержит бизнес-логики.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Карта инструментов v1
|
||||||
|
|
||||||
|
Сгруппированы вокруг центрального цикла «эскиз ↔ операция». Имена — `snake_case`.
|
||||||
|
|
||||||
|
**System**
|
||||||
|
- `kompas_connect` — attach/launch, возвращает версию и редакцию (`GetSystemVersion`, `ApplicationName`).
|
||||||
|
- `kompas_set_visible` — показать/скрыть окно (`IApplication.Visible`).
|
||||||
|
- `kompas_status` — состояние соединения, активный документ.
|
||||||
|
|
||||||
|
**Documents** (`IApplication.Documents` → `IDocuments`)
|
||||||
|
- `document_create` — тип `part | assembly | drawing | fragment` (`DocumentTypeEnum`: Part=4, Assembly=5, Drawing=1, Fragment=2).
|
||||||
|
- `document_open` / `document_save` / `document_close` / `document_active`.
|
||||||
|
|
||||||
|
**Sketch** (2D-геометрия внутри 3D-эскиза)
|
||||||
|
- `sketch_create` — на базовой плоскости (`o3d_planeXOY` / `XOZ` / `YOZ`) или на выбранной грани.
|
||||||
|
- `sketch_add_line` / `sketch_add_circle` / `sketch_add_arc` / `sketch_add_rectangle`.
|
||||||
|
- `sketch_close` — завершить редактирование.
|
||||||
|
- Жизненный цикл: `NewEntity(o3d_sketch)` → `SetPlane(plane|face)` → `BeginEdit()` →
|
||||||
|
`ksLineSeg` / `ksCircle` / … → `EndEdit()`.
|
||||||
|
|
||||||
|
**Features** (3D-операции)
|
||||||
|
- `extrude_boss` / `extrude_cut` — sketch, depth, direction, endType.
|
||||||
|
- `revolve_boss` / `revolve_cut` — угол.
|
||||||
|
- `rebuild` — перестроить деталь.
|
||||||
|
|
||||||
|
**Selection / Query**
|
||||||
|
- `select_face_by_point` — выбрать грань точкой (для следующего эскиза).
|
||||||
|
- `list_bodies`, `list_faces_planes` — перечислить тела/грани и плоскости.
|
||||||
|
- `get_part_info` — МЦХ/габариты (`IMassInertiaParam7`).
|
||||||
|
|
||||||
|
**Vision — визуальная обратная связь («зрение агента»)**
|
||||||
|
- `model_snapshot` — отрендерить активный 3D-документ в PNG и вернуть **как image-контент MCP**,
|
||||||
|
чтобы мультимодальный агент *увидел* промежуточный результат и контролировал построение пошагово.
|
||||||
|
- `set_view` — задать ориентацию вида (`iso` / `front` / `top` / `right` / …), zoom-to-fit и режим
|
||||||
|
отображения (`ShadedWireframe`) перед снимком — иначе снимок берётся со случайного текущего ракурса.
|
||||||
|
|
||||||
|
> **Механизм.** `ksDocument3D.RasterFormatParam()` → `IRasterFormatParam` рендерит документ в растр;
|
||||||
|
> свойство **`resultArrayBytes`** возвращает изображение как **массив байт в память** (без временного
|
||||||
|
> файла). Альтернатива — экспортные функции `ksSaveAsToRasterFormat[W]`. Полученные байты сервер
|
||||||
|
> упаковывает в image-контент MCP-ответа (C# SDK `ModelContextProtocol` это поддерживает).
|
||||||
|
> Параметры: формат (PNG/JPEG/BMP/TIFF), разрешение, цвет/`greyScale`.
|
||||||
|
> Снимки делать после значимых операций (не после каждой линии) — каждый кадр расходует
|
||||||
|
> токены изображения в контексте агента. На v24 Home качество/доступность рендера проверить на прототипе.
|
||||||
|
|
||||||
|
**Escape hatch (опционально)**
|
||||||
|
- `run_kompas_command` — `IApplication.ExecuteKompasCommand(commandID, post)` для команд,
|
||||||
|
ещё не обёрнутых в типизированные инструменты. Доступность проверять через
|
||||||
|
`IsKompasCommandEnable`.
|
||||||
|
|
||||||
|
### Замечание об API5 vs API7 для 3D-операций
|
||||||
|
|
||||||
|
API7 создаёт операции через `IPart7` → (QueryInterface) `IModelContainer` → `IExtrusions.Add()`
|
||||||
|
→ `IExtrusion`. Примеры SDK при этом активно используют **API5-путь** (`ksPart.NewEntity(o3d_…)` →
|
||||||
|
`ksBossExtrusionDefinition.SetSideParam/SetSketch` → `Create()`), который хорошо документирован.
|
||||||
|
**Стратегия v1:** предпочитать API7 там, где он покрывает операцию; падать на API5 `ksPart`-путь
|
||||||
|
там, где он проще/надёжнее (мост — `kompas.TransferInterface`). Выбор фиксировать в `Core`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Центральный сценарий: цикл «эскиз → операция → эскиз»
|
||||||
|
|
||||||
|
Основной пользовательский поток v1 — итеративное параметрическое моделирование.
|
||||||
|
|
||||||
|
```
|
||||||
|
document_create(part)
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
sketch_create(plane = XOY) ──► sketch_add_circle(...) ──► sketch_close
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
extrude_boss(sketch, depth=…, dir=dtNormal, end=etBlind) → цилиндр
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
set_view(iso, fit) → model_snapshot 👁 агент СМОТРИТ на результат и решает следующий шаг
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
select_face_by_point(x,y,z) ← выбираем верхнюю грань
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
sketch_create(face = выбранная) ──► sketch_add_circle(...) ──► sketch_close
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
extrude_cut(sketch, depth=…) → отверстие
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
model_snapshot 👁 проверка результата
|
||||||
|
│
|
||||||
|
└──────────► повторять: смотрим → новая грань → новый эскиз → новая операция
|
||||||
|
```
|
||||||
|
|
||||||
|
Соответствие API (на STA-потоке, через `Core`):
|
||||||
|
|
||||||
|
1. `Document3D()` / `IKompasDocument3D` → `IPart7` (вершинная деталь, `pTop_Part`).
|
||||||
|
2. `part.NewEntity(o3d_sketch)` → `GetDefinition()` → `SetPlane(базовая_плоскость | грань)` →
|
||||||
|
`Create()`; `BeginEdit()` → рисование примитивов (`ksLineSeg`, `ksCircle`, `ksArcByPoint`, …) →
|
||||||
|
`EndEdit()`.
|
||||||
|
3. `part.NewEntity(o3d_bossExtrusion)` → `ksBossExtrusionDefinition` →
|
||||||
|
`SetSideParam(direction, ksEndTypeEnum, depth, …)` → `SetSketch(эскиз)` → `Create()`.
|
||||||
|
Для выреза — `o3d_cutExtrusion`; для вращения — `o3d_bossRotated`/`o3d_cutRotated`.
|
||||||
|
4. Выбор грани для следующего эскиза:
|
||||||
|
`part.EntityCollection(o3d_face)` → `SelectByPoint(x, y, z)` → элемент коллекции как плоскость
|
||||||
|
эскиза.
|
||||||
|
5. Снимок результата: `set_view` (ориентация + fit) → `ksDocument3D.RasterFormatParam()` →
|
||||||
|
`IRasterFormatParam.resultArrayBytes` → image-контент MCP. Агент анализирует кадр и выбирает
|
||||||
|
следующий шаг.
|
||||||
|
|
||||||
|
Используемые перечисления: `ksDirectionTypeEnum` (`dtNormal`/`dtReverse`/`dtBoth`/`dtMiddlePlane`),
|
||||||
|
`ksEndTypeEnum` (`etBlind`/`etThroughAll`/`etUpTo…`), `Obj3dType` (`o3d_sketch`, `o3d_bossExtrusion`,
|
||||||
|
`o3d_cutExtrusion`, `o3d_face`, `o3d_planeXOY`, …).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Обработка ошибок и безопасность
|
||||||
|
|
||||||
|
- **Трансляция ошибок COM** в структурированные MCP-ошибки: проверять HRESULT каждого вызова и
|
||||||
|
опрашивать `kompas.ksGetLastError()` / `IApplication.KompasError`; возвращать клиенту код + текст.
|
||||||
|
- **Null-чек после каждого приведения** COM-интерфейса (любой каст может вернуть `null`).
|
||||||
|
- **`.Init()` на param-структурах** перед использованием (`GetParamStruct(...)` → `.Init()`).
|
||||||
|
- **Гарантированный `EndEdit`** для эскизов через `try/finally` — незакрытый эскиз ломает документ.
|
||||||
|
- **Подтверждение деструктивных операций**: закрытие без сохранения, перезапись файла, `Quit` —
|
||||||
|
только по явному флагу в аргументах инструмента.
|
||||||
|
- **Модель состояния**: инструменты по умолчанию работают с «активным документом»; опционально —
|
||||||
|
явный хэндл/идентификатор документа.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Сборка и запуск
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
dotnet build -c Release -r win-x64 # сборка
|
||||||
|
# → kompas-mcp.exe (self-contained, win-x64)
|
||||||
|
```
|
||||||
|
|
||||||
|
Регистрация stdio-сервера у клиента (пример для конфигурации Claude Desktop):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"kompas3d": { "command": "C:\\path\\to\\kompas-mcp.exe", "args": [] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ручная проверка (smoke):** запустить КОМПАС (или дать серверу запустить его) → через инструменты
|
||||||
|
построить простую деталь (выдавить окружность в цилиндр, затем `extrude_cut` — отверстие) →
|
||||||
|
убедиться визуально в окне КОМПАС → сохранить `.m3d`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Стратегия тестирования
|
||||||
|
|
||||||
|
- **Интеграционные тесты** (требуют установленного КОМПАС): xUnit, категория `Integration`,
|
||||||
|
гейтятся (не идут в обычном CI без КОМПАС). Покрывают связку Core↔COM и центральный цикл.
|
||||||
|
- **Unit-тесты** чистой логики **без COM**: маппинг строк↔перечислений, валидация аргументов,
|
||||||
|
форматирование результатов/ошибок.
|
||||||
|
- **Smoke MCP**: проверка протокола через **MCP Inspector** или тестовый клиент (list tools, вызов
|
||||||
|
`kompas_connect`/`document_create`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Дорожная карта (после v1)
|
||||||
|
|
||||||
|
1. Операции: `fillet` / `chamfer` / `shell` / `rib` / `loft` / `sweep`; массивы
|
||||||
|
(`ILinearPattern`, `ICircularPattern`).
|
||||||
|
2. Параметры и свойства: `IVariable7` / `IVariableTable`, `IPropertyMng` / `IPropertyKeeper`.
|
||||||
|
3. Полноценное 2D-черчение: виды, линии/дуги/окружности, размеры (`ILinearDimension`, …),
|
||||||
|
штриховки, тексты.
|
||||||
|
4. Спецификации (`ISpecification`), сборки и сопряжения (`IAssemblyDocument`).
|
||||||
|
5. Конвертеры/экспорт (STEP и др.) через `IApplication.Converter`.
|
||||||
|
6. Транспорт **HTTP/SSE** — как опция для удалённых клиентов.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Открытые вопросы
|
||||||
|
|
||||||
|
- **Один проект vs solution из 4 проектов** в v1 (склоняемся к одному solution, слои — папками,
|
||||||
|
выделять проекты по мере роста).
|
||||||
|
- **Embed Interop Types vs генерируемые interop-сборки** (`tlbimp`) — проверить на реальных
|
||||||
|
`.tlb` v24, выбрать по совместимости типов и размеру.
|
||||||
|
- **Стратегия именования инструментов** — подтвердить `snake_case` с группами-префиксами
|
||||||
|
(`document_*`, `sketch_*`, …).
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
# План реализации MCP-сервера КОМПАС-3D
|
||||||
|
|
||||||
|
> Документ описывает, **как** строить сервер. Что строим и почему — в
|
||||||
|
> [`ARCHITECTURE.md`](ARCHITECTURE.md). Прогресс отражается в
|
||||||
|
> [`presentation.html`](presentation.html) (TODO).
|
||||||
|
|
||||||
|
## Принцип
|
||||||
|
|
||||||
|
**«Ходячий скелет» + спайки на риски.** Сначала сквозной тонкий срез
|
||||||
|
(подключение → снимок), который проходит через все слои и доказывает, что связка
|
||||||
|
COM ↔ STA ↔ MCP работает. Затем наращиваем инструменты, и **каждый шаг построения сразу
|
||||||
|
проверяем визуально** (`model_snapshot`) — это и есть целевой UX «строю под контролем».
|
||||||
|
|
||||||
|
## Окружение (проверено 2026-05-25)
|
||||||
|
|
||||||
|
| | Факт | Следствие для плана |
|
||||||
|
|---|---|---|
|
||||||
|
| .NET SDK | **10.0.108** (отдельного .NET 8 SDK нет; рантайм 8 есть) | таргет **`net8.0-windows`** (LTS), собирается SDK 10 |
|
||||||
|
| ProgID КОМПАС | **`KOMPAS.Application.7`** и **`KOMPAS.Application.5`** зарегистрированы; `KOMPASLT.*` **отсутствует** | подключаться напрямую к API7 через `KOMPAS.Application.7`; LT — только запасной |
|
||||||
|
| Процесс | КОМПАС не запущен | спайк подключения должен уметь **launch**, не только attach |
|
||||||
|
| SDK | `C:\Program Files\ASCON\KOMPAS-3D v24 Home\SDK` (`lib\*.tlb`) | источник interop |
|
||||||
|
|
||||||
|
**Прерэквизит разработчика:** установленный КОМПАС v24 + возможность его запускать.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Спайки (делать первыми — снимают главные неизвестные)
|
||||||
|
|
||||||
|
| # | Риск / вопрос | Критерий успеха |
|
||||||
|
|---|---|---|
|
||||||
|
| **S1** | Генерация COM-interop из `.tlb` в SDK-проекте | `net8.0-windows` проект с `<COMReference>` на `kAPI7.tlb`, `kAPI5.tlb`, `ksConstants.tlb`, `ksConstants3D.tlb` собирается; типы `IApplication`, `KompasObject` доступны. Запасной путь — `tlbimp`/ручные interop-сборки |
|
||||||
|
| **S2** | Подключение по ProgID | консольная проба: attach (`Marshal.GetActiveObject`) → при отказе launch (`Activator.CreateInstance(Type.GetTypeFromProgID("KOMPAS.Application.7"))`); получить `IApplication`, `Visible=true`, прочитать версию |
|
||||||
|
| **S3** | STA-поток + насос сообщений + async MCP | выделенный STA-поток выполняет COM-вызовы из пула задач без `RPC_E_WRONGTHREAD`; насос сообщений не вешает процесс |
|
||||||
|
| **S4** | Рендер модели в растр на v24 Home | `ksDocument3D.RasterFormatParam()` → `resultArrayBytes` отдаёт корректные PNG-байты открытого 3D-документа |
|
||||||
|
|
||||||
|
Спайки — выбрасываемый код в `spikes/`; выводы переносятся в `Core`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Фазы
|
||||||
|
|
||||||
|
### Фаза 0 — Каркас репозитория
|
||||||
|
- `git init`, `.gitignore` (.NET + `bin/`, `obj/`; **не** игнорировать `docs/`).
|
||||||
|
- `KompasMcp.sln`; проекты `Kompas.Mcp.Host`, `Kompas.Mcp.Core`, `Kompas.Mcp.Interop`, `Kompas.Mcp.Tools`
|
||||||
|
(в v1 допустимо начать одним проектом, разнося по папкам).
|
||||||
|
- `Directory.Build.props`: `TargetFramework=net8.0-windows`, `Platforms=x64`, `Nullable=enable`, `LangVersion=latest`.
|
||||||
|
- Пакеты: `ModelContextProtocol`, `Microsoft.Extensions.Hosting`.
|
||||||
|
- **Готово:** `dotnet build -c Release` проходит; пустой stdio-хост стартует.
|
||||||
|
|
||||||
|
### Фаза 1 — Interop + маппинг констант
|
||||||
|
- `<COMReference>` на типобиблиотеки (итог S1). Зафиксировать стратегию (embed vs сборки).
|
||||||
|
- `Kompas.Mcp.Interop`: хелперы «строка ↔ enum» — `DocumentTypeEnum` (part=4, assembly=5, drawing=1, fragment=2),
|
||||||
|
`Obj3dType`, `ksEndTypeEnum`, `ksDirectionTypeEnum`, базовые плоскости (`o3d_planeXOY/XOZ/YOZ`).
|
||||||
|
- **Готово:** unit-тесты маппинга (без COM) зелёные.
|
||||||
|
|
||||||
|
### Фаза 2 — STA-диспетчер
|
||||||
|
- `KompasThread`: выделенный STA-поток, очередь задач (`Channel`/`BlockingCollection`),
|
||||||
|
`Task<T> InvokeAsync(Func<T>)`, насос сообщений + периодический `PumpWaitingMessages` (итог S3).
|
||||||
|
- **Готово:** интеграционный тест — параллельные `InvokeAsync` исполняются строго на одном STA-потоке.
|
||||||
|
|
||||||
|
### Фаза 3 — Connection + вертикальный срез (MCP)
|
||||||
|
- `KompasSession`: resolve ProgID (`KOMPAS.Application.7` → `.5`+`ksGetApplication7()` → `KOMPASLT.*`),
|
||||||
|
attach/launch, `Visible`, версия/редакция, освобождение COM, `Quit` только если запускали мы.
|
||||||
|
- Хост: stdio-сервер, **логи в stderr**, регистрация инструментов через DI.
|
||||||
|
- Инструменты `System`: `kompas_connect`, `kompas_status`, `kompas_set_visible`.
|
||||||
|
- **Готово (веха «скелет»):** из MCP Inspector вызвать `kompas_connect` → КОМПАС стартует и виден; `kompas_status` отвечает.
|
||||||
|
|
||||||
|
### Фаза 4 — Documents
|
||||||
|
- `document_create` (part/assembly/drawing/fragment), `document_open`, `document_save`, `document_close`, `document_active`.
|
||||||
|
- **Готово:** создать деталь, сохранить `.m3d`, закрыть, открыть заново.
|
||||||
|
|
||||||
|
### Фаза 5 — Vision (раннее — делает всё дальнейшее проверяемым)
|
||||||
|
- `set_view` (ориентация iso/front/top/right, zoom-to-fit, `ShadedWireframe`), `model_snapshot`
|
||||||
|
(`RasterFormatParam.resultArrayBytes` → image-контент MCP) — итог S4.
|
||||||
|
- **Готово:** `model_snapshot` возвращает PNG открытой детали; агент видит изображение в ответе инструмента.
|
||||||
|
|
||||||
|
### Фаза 6 — Sketch
|
||||||
|
- `sketch_create` (плоскость или грань), `sketch_add_line|circle|arc|rectangle`, `sketch_close`
|
||||||
|
(`NewEntity(o3d_sketch)`→`SetPlane`→`BeginEdit`/примитивы/`EndEdit`, гарантированный `EndEdit` в `finally`).
|
||||||
|
- **Готово:** построить эскиз-окружность на XOY и увидеть его на снимке.
|
||||||
|
|
||||||
|
### Фаза 7 — Features
|
||||||
|
- `extrude_boss`/`extrude_cut` (depth, direction, endType), `revolve_boss`/`revolve_cut`, `rebuild`.
|
||||||
|
- **Готово (целевой демо-сценарий):** окружность → `extrude_boss` (цилиндр) → `model_snapshot` →
|
||||||
|
выбрать грань → эскиз → `extrude_cut` (отверстие) → `model_snapshot`. Полный цикл со зрением.
|
||||||
|
|
||||||
|
### Фаза 8 — Query / Selection
|
||||||
|
- `select_face_by_point`, `list_bodies`, `list_faces_planes`, `get_part_info` (МЦХ/габариты).
|
||||||
|
- **Готово:** агент может выбрать грань для следующего эскиза по координатам.
|
||||||
|
|
||||||
|
### Фаза 9 — Качество и поставка
|
||||||
|
- Unit-тесты (маппинг, валидация, форматирование); интеграционные (`[Trait("Category","Integration")]`, гейтятся).
|
||||||
|
- Трансляция ошибок COM (`ksGetLastError`/`IApplication.KompasError`) в структурированные MCP-ошибки.
|
||||||
|
- Сборка `kompas-mcp.exe` (`dotnet publish -c Release -r win-x64`), регистрация stdio-сервера в конфиге клиента.
|
||||||
|
- Escape-hatch `run_kompas_command` (опц.).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Порядок и зависимости
|
||||||
|
|
||||||
|
```
|
||||||
|
S1,S2 ─► Фаза 0 ─► Фаза 1 ─► Фаза 2 ─► Фаза 3 (скелет) ─► Фаза 4 ─► Фаза 5 (зрение)
|
||||||
|
│
|
||||||
|
Фаза 6 ─► Фаза 7 ─► Фаза 8 ─► Фаза 9
|
||||||
|
S3 ─► Фаза 2 S4 ─► Фаза 5
|
||||||
|
```
|
||||||
|
|
||||||
|
Критический риск — **S1/S3** (interop + STA). Если `<COMReference>` не заработает на v24 `.tlb` —
|
||||||
|
переход на сгенерированные `tlbimp` interop-сборки. Все спайки до Фазы 3.
|
||||||
|
|
||||||
|
## Definition of Done для v1
|
||||||
|
|
||||||
|
Агент через MCP: подключается к КОМПАС → создаёт деталь → строит эскиз → выдавливает →
|
||||||
|
делает снимок → видит результат → выбирает грань → повторяет → сохраняет `.m3d`.
|
||||||
|
Сервер собирается в один `.exe` и регистрируется в клиенте по stdio.
|
||||||
@@ -0,0 +1,344 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>КОМПАС-3D MCP — управление CAD через LLM</title>
|
||||||
|
<style>
|
||||||
|
:root{
|
||||||
|
--bg:#0b1020; --bg2:#121a33; --card:#161f3d; --card2:#1b264a;
|
||||||
|
--txt:#e8edf7; --muted:#9aa7c7; --line:#26335c;
|
||||||
|
--accent:#4da3ff; --accent2:#7c5cff; --green:#36d399; --amber:#fbbd23;
|
||||||
|
--grad:linear-gradient(135deg,#4da3ff 0%,#7c5cff 100%);
|
||||||
|
}
|
||||||
|
*{box-sizing:border-box;margin:0;padding:0}
|
||||||
|
html{scroll-behavior:smooth}
|
||||||
|
body{
|
||||||
|
font-family:"Segoe UI",system-ui,-apple-system,Roboto,Arial,sans-serif;
|
||||||
|
background:radial-gradient(1200px 600px at 80% -10%,#1c2750 0%,var(--bg) 55%) no-repeat,var(--bg);
|
||||||
|
color:var(--txt);line-height:1.6;-webkit-font-smoothing:antialiased;
|
||||||
|
}
|
||||||
|
.wrap{max-width:1120px;margin:0 auto;padding:0 24px}
|
||||||
|
section{padding:64px 0}
|
||||||
|
h2{font-size:30px;margin-bottom:8px;letter-spacing:-.5px}
|
||||||
|
.sub{color:var(--muted);margin-bottom:36px;max-width:640px}
|
||||||
|
.eyebrow{color:var(--accent);font-weight:700;font-size:13px;letter-spacing:2px;text-transform:uppercase}
|
||||||
|
|
||||||
|
/* HERO */
|
||||||
|
.hero{padding:96px 0 72px;position:relative;overflow:hidden}
|
||||||
|
.hero::after{content:"";position:absolute;inset:0;background:
|
||||||
|
radial-gradient(600px 300px at 15% 20%,rgba(124,92,255,.18),transparent 60%);
|
||||||
|
pointer-events:none}
|
||||||
|
.badge{display:inline-flex;align-items:center;gap:8px;background:rgba(77,163,255,.1);
|
||||||
|
border:1px solid var(--line);color:var(--accent);padding:6px 14px;border-radius:999px;
|
||||||
|
font-size:13px;font-weight:600;margin-bottom:22px}
|
||||||
|
.dot{width:8px;height:8px;border-radius:50%;background:var(--amber);box-shadow:0 0 10px var(--amber)}
|
||||||
|
.hero h1{font-size:54px;line-height:1.05;letter-spacing:-1.5px;margin-bottom:18px;font-weight:800}
|
||||||
|
.hero h1 .g{background:var(--grad);-webkit-background-clip:text;background-clip:text;color:transparent}
|
||||||
|
.hero p.lead{font-size:19px;color:var(--muted);max-width:680px;margin-bottom:30px}
|
||||||
|
.chips{display:flex;flex-wrap:wrap;gap:10px}
|
||||||
|
.chip{background:var(--card);border:1px solid var(--line);border-radius:10px;
|
||||||
|
padding:8px 14px;font-size:13px;font-weight:600;color:#cdd7f2}
|
||||||
|
.chip b{color:var(--accent)}
|
||||||
|
|
||||||
|
/* GRID CARDS */
|
||||||
|
.grid{display:grid;grid-template-columns:repeat(3,1fr);gap:18px}
|
||||||
|
.card{background:linear-gradient(180deg,var(--card),var(--card2));border:1px solid var(--line);
|
||||||
|
border-radius:16px;padding:24px;transition:transform .2s,border-color .2s}
|
||||||
|
.card:hover{transform:translateY(-4px);border-color:var(--accent)}
|
||||||
|
.card .ic{width:44px;height:44px;border-radius:12px;background:var(--grad);
|
||||||
|
display:flex;align-items:center;justify-content:center;font-size:22px;margin-bottom:14px}
|
||||||
|
.card h3{font-size:17px;margin-bottom:6px}
|
||||||
|
.card p{color:var(--muted);font-size:14px}
|
||||||
|
.card code{background:rgba(255,255,255,.06);padding:1px 6px;border-radius:5px;font-size:12px;color:#cdd7f2}
|
||||||
|
|
||||||
|
/* WORKFLOW */
|
||||||
|
.flow{display:flex;align-items:stretch;gap:0;flex-wrap:wrap;margin-top:8px}
|
||||||
|
.step{flex:1;min-width:150px;background:var(--card);border:1px solid var(--line);
|
||||||
|
border-radius:14px;padding:18px 16px;position:relative;text-align:center}
|
||||||
|
.step .n{font-size:12px;color:var(--accent);font-weight:700}
|
||||||
|
.step h4{font-size:15px;margin:6px 0 4px}
|
||||||
|
.step p{font-size:12.5px;color:var(--muted)}
|
||||||
|
.arrow{display:flex;align-items:center;justify-content:center;color:var(--accent2);
|
||||||
|
font-size:24px;padding:0 8px}
|
||||||
|
.loopnote{margin-top:14px;color:var(--muted);font-size:14px;text-align:center}
|
||||||
|
.loopnote b{color:var(--green)}
|
||||||
|
|
||||||
|
/* STACK */
|
||||||
|
.stack{display:grid;grid-template-columns:repeat(4,1fr);gap:14px}
|
||||||
|
.scard{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:18px;text-align:center}
|
||||||
|
.scard .k{font-size:12px;color:var(--muted);text-transform:uppercase;letter-spacing:1px}
|
||||||
|
.scard .v{font-size:18px;font-weight:700;margin-top:6px;color:#dfe7fb}
|
||||||
|
.scard .v small{display:block;font-size:12px;color:var(--muted);font-weight:500;margin-top:3px}
|
||||||
|
|
||||||
|
/* TODO */
|
||||||
|
.todo-grid{display:grid;grid-template-columns:1fr 1fr;gap:24px}
|
||||||
|
.panel{background:linear-gradient(180deg,var(--card),var(--card2));border:1px solid var(--line);
|
||||||
|
border-radius:16px;padding:26px}
|
||||||
|
.panel h3{font-size:18px;margin-bottom:4px;display:flex;align-items:center;gap:10px}
|
||||||
|
.tag{font-size:11px;font-weight:700;padding:3px 9px;border-radius:999px;letter-spacing:.5px}
|
||||||
|
.tag.done{background:rgba(54,211,153,.15);color:var(--green);border:1px solid rgba(54,211,153,.3)}
|
||||||
|
.tag.now{background:rgba(251,189,35,.15);color:var(--amber);border:1px solid rgba(251,189,35,.3)}
|
||||||
|
.tag.next{background:rgba(124,92,255,.15);color:#b9a6ff;border:1px solid rgba(124,92,255,.3)}
|
||||||
|
.panel .meta{color:var(--muted);font-size:13px;margin-bottom:16px}
|
||||||
|
ul.list{list-style:none}
|
||||||
|
ul.list li{display:flex;gap:11px;padding:8px 0;border-bottom:1px dashed var(--line);font-size:14.5px}
|
||||||
|
ul.list li:last-child{border-bottom:0}
|
||||||
|
.mark{flex:0 0 20px;width:20px;height:20px;border-radius:6px;display:flex;align-items:center;
|
||||||
|
justify-content:center;font-size:12px;font-weight:700;margin-top:2px}
|
||||||
|
.mark.ok{background:var(--green);color:#04231a}
|
||||||
|
.mark.todo{background:transparent;border:1.5px solid var(--line)}
|
||||||
|
.mark.wip{background:var(--amber);color:#3a2c00}
|
||||||
|
li.muted span.t{color:var(--muted)}
|
||||||
|
|
||||||
|
/* progress */
|
||||||
|
.prog{margin:18px 0 4px}
|
||||||
|
.bar{height:10px;border-radius:999px;background:#0e1530;overflow:hidden;border:1px solid var(--line)}
|
||||||
|
.bar > i{display:block;height:100%;background:var(--grad);border-radius:999px}
|
||||||
|
.progrow{display:flex;justify-content:space-between;font-size:13px;color:var(--muted);margin-bottom:8px}
|
||||||
|
|
||||||
|
/* roadmap timeline */
|
||||||
|
.timeline{display:grid;grid-template-columns:repeat(5,1fr);gap:14px;margin-top:8px}
|
||||||
|
.tl{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:18px 16px;position:relative}
|
||||||
|
.tl .ph{font-size:12px;font-weight:700;color:var(--accent)}
|
||||||
|
.tl h4{font-size:15px;margin:8px 0 6px}
|
||||||
|
.tl p{font-size:12.5px;color:var(--muted)}
|
||||||
|
.tl.cur{border-color:var(--amber);box-shadow:0 0 0 1px rgba(251,189,35,.25)}
|
||||||
|
|
||||||
|
footer{border-top:1px solid var(--line);padding:34px 0;color:var(--muted);font-size:14px}
|
||||||
|
footer a{color:var(--accent);text-decoration:none}
|
||||||
|
footer a:hover{text-decoration:underline}
|
||||||
|
.links{display:flex;gap:20px;flex-wrap:wrap;margin-top:10px}
|
||||||
|
|
||||||
|
@media(max-width:880px){
|
||||||
|
.grid,.stack{grid-template-columns:1fr 1fr}
|
||||||
|
.todo-grid{grid-template-columns:1fr}
|
||||||
|
.timeline{grid-template-columns:1fr 1fr}
|
||||||
|
.hero h1{font-size:40px}
|
||||||
|
.arrow{transform:rotate(90deg)}
|
||||||
|
}
|
||||||
|
@media(max-width:520px){.grid,.stack,.timeline{grid-template-columns:1fr}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- HERO -->
|
||||||
|
<header class="hero">
|
||||||
|
<div class="wrap">
|
||||||
|
<span class="badge"><span class="dot" style="background:var(--green);box-shadow:0 0 10px var(--green)"></span> Статус: спайки S1–S2 пройдены · подключение к КОМПАС работает</span>
|
||||||
|
<h1>КОМПАС-3D <span class="g">MCP-сервер</span><br>управление CAD языком LLM</h1>
|
||||||
|
<p class="lead">MCP-сервер, который превращает операции КОМПАС-3D — создание документов,
|
||||||
|
эскизы, 3D-операции, параметры — в инструменты для языковой модели. Под капотом —
|
||||||
|
COM Automation API АСКОН.</p>
|
||||||
|
<div class="chips">
|
||||||
|
<span class="chip"><b>.NET 8</b> · C#</span>
|
||||||
|
<span class="chip">транспорт <b>stdio</b></span>
|
||||||
|
<span class="chip"><b>COM</b> Automation · API7 / API5</span>
|
||||||
|
<span class="chip">Windows <b>x64</b></span>
|
||||||
|
<span class="chip">КОМПАС-3D <b>v24 Home</b></span>
|
||||||
|
<span class="chip">👁 <b>зрение агента</b></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- KEY FEATURES -->
|
||||||
|
<section id="features">
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="eyebrow">Возможности</div>
|
||||||
|
<h2>Что умеет MCP-сервер</h2>
|
||||||
|
<p class="sub">Инструменты сгруппированы вокруг живого инженерного цикла моделирования —
|
||||||
|
от подключения к КОМПАС до построения и опроса детали.</p>
|
||||||
|
<div class="grid">
|
||||||
|
<div class="card">
|
||||||
|
<div class="ic">🔌</div>
|
||||||
|
<h3>Подключение к КОМПАС</h3>
|
||||||
|
<p>Attach к запущенному или launch нового экземпляра по ProgID, переход на API7
|
||||||
|
через <code>ksGetApplication7()</code>, управление видимостью окна.</p>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="ic">📄</div>
|
||||||
|
<h3>Документы</h3>
|
||||||
|
<p>Создание, открытие, сохранение и закрытие деталей, сборок, чертежей и фрагментов
|
||||||
|
через <code>IDocuments</code>.</p>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="ic">✏️</div>
|
||||||
|
<h3>Эскизы</h3>
|
||||||
|
<p>Эскиз на плоскости или грани, примитивы (линия, окружность, дуга,
|
||||||
|
прямоугольник), безопасный цикл <code>BeginEdit → EndEdit</code>.</p>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="ic">🧊</div>
|
||||||
|
<h3>3D-операции</h3>
|
||||||
|
<p>Выдавливание и вырез, вращение, перестроение. Управление направлением, глубиной и
|
||||||
|
типом окончания (<code>ksEndTypeEnum</code>).</p>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="ic">🎯</div>
|
||||||
|
<h3>Выборка и запросы</h3>
|
||||||
|
<p>Выбор грани точкой для следующего эскиза, перечисление тел и граней,
|
||||||
|
массово-центровочные характеристики детали.</p>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="ic">👁️</div>
|
||||||
|
<h3>Зрение агента</h3>
|
||||||
|
<p>Рендер модели в PNG прямо в память (<code>resultArrayBytes</code>) и возврат как
|
||||||
|
image-контент MCP — агент <b>видит</b> результат и строит пошагово. Плюс <code>set_view</code>:
|
||||||
|
ориентация и zoom-to-fit перед снимком.</p>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="ic">🛟</div>
|
||||||
|
<h3>Escape-hatch</h3>
|
||||||
|
<p>Прямой вызов команд КОМПАС через <code>ExecuteKompasCommand</code> для того,
|
||||||
|
что ещё не обёрнуто в типизированные инструменты.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- WORKFLOW -->
|
||||||
|
<section id="workflow" style="background:linear-gradient(180deg,transparent,rgba(124,92,255,.05))">
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="eyebrow">Ядро сценария</div>
|
||||||
|
<h2>Цикл «эскиз → операция → эскиз»</h2>
|
||||||
|
<p class="sub">Главный пользовательский поток v1 — итеративное параметрическое моделирование
|
||||||
|
с визуальной проверкой: рисуем профиль, превращаем в объём, <b>смотрим на результат</b>,
|
||||||
|
выбираем грань, рисуем следующий профиль.</p>
|
||||||
|
<div class="flow">
|
||||||
|
<div class="step"><div class="n">01</div><h4>Документ</h4><p>создать деталь (part)</p></div>
|
||||||
|
<div class="arrow">→</div>
|
||||||
|
<div class="step"><div class="n">02</div><h4>Эскиз</h4><p>на плоскости XOY, профиль</p></div>
|
||||||
|
<div class="arrow">→</div>
|
||||||
|
<div class="step"><div class="n">03</div><h4>Выдавить</h4><p>boss-операция → тело</p></div>
|
||||||
|
<div class="arrow">→</div>
|
||||||
|
<div class="step" style="border-color:var(--accent2)"><div class="n" style="color:var(--accent2)">04 👁</div><h4>Снимок</h4><p>агент видит результат</p></div>
|
||||||
|
<div class="arrow">→</div>
|
||||||
|
<div class="step"><div class="n">05</div><h4>Выбрать грань</h4><p>по точке на теле</p></div>
|
||||||
|
<div class="arrow">→</div>
|
||||||
|
<div class="step"><div class="n">06</div><h4>Вырез</h4><p>новый эскиз → cut</p></div>
|
||||||
|
</div>
|
||||||
|
<p class="loopnote">↻ <b>Повторять со зрением</b> — снимок → новая грань → новый эскиз → новая операция → снова снимок, пока деталь не готова</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- STACK -->
|
||||||
|
<section id="stack">
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="eyebrow">Технологии</div>
|
||||||
|
<h2>Стек решения</h2>
|
||||||
|
<p class="sub">Выбор обоснован первоклассным COM-интеропом .NET и официальным C# MCP SDK —
|
||||||
|
подробности в документе архитектуры.</p>
|
||||||
|
<div class="stack">
|
||||||
|
<div class="scard"><div class="k">Платформа</div><div class="v">.NET 8<small>C# · win-x64</small></div></div>
|
||||||
|
<div class="scard"><div class="k">MCP SDK</div><div class="v">ModelContextProtocol<small>транспорт stdio</small></div></div>
|
||||||
|
<div class="scard"><div class="k">Интероп</div><div class="v">COM Automation<small>API7 + API5 (.tlb)</small></div></div>
|
||||||
|
<div class="scard"><div class="k">Потоки</div><div class="v">STA-поток<small>насос сообщений</small></div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- TODO -->
|
||||||
|
<section id="todo" style="background:linear-gradient(180deg,transparent,rgba(77,163,255,.05))">
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="eyebrow">Прогресс</div>
|
||||||
|
<h2>TODO проекта</h2>
|
||||||
|
<p class="sub">Честный срез состояния: фундамент подготовки готов, реализация сервера —
|
||||||
|
впереди.</p>
|
||||||
|
|
||||||
|
<div class="prog">
|
||||||
|
<div class="progrow"><span>Общий прогресс</span><span>Спайки пройдены · идёт фундамент v1</span></div>
|
||||||
|
<div class="bar"><i style="width:34%"></i></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="todo-grid" style="margin-top:26px">
|
||||||
|
<!-- DONE -->
|
||||||
|
<div class="panel">
|
||||||
|
<h3>Подготовка <span class="tag done">ГОТОВО</span></h3>
|
||||||
|
<p class="meta">Исследование, документация и инструменты разработчика</p>
|
||||||
|
<ul class="list">
|
||||||
|
<li class="muted"><span class="mark ok">✓</span><span class="t">Исследование SDK и COM API (API5/API7, паттерны подключения)</span></li>
|
||||||
|
<li class="muted"><span class="mark ok">✓</span><span class="t">CLAUDE.md — гайд по репозиторию и API</span></li>
|
||||||
|
<li class="muted"><span class="mark ok">✓</span><span class="t">Навигатор по справке: <code>tools/kdoc.py</code> + индекс на 26k стр.</span></li>
|
||||||
|
<li class="muted"><span class="mark ok">✓</span><span class="t">Документ архитектуры <code>docs/ARCHITECTURE.md</code></span></li>
|
||||||
|
<li class="muted"><span class="mark ok">✓</span><span class="t">Выбор стека: .NET/C#, stdio, цикл «эскиз↔операция»</span></li>
|
||||||
|
<li class="muted"><span class="mark ok">✓</span><span class="t">Решение о визуальной обратной связи (зрение агента, рендер в растр)</span></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- NOW -->
|
||||||
|
<div class="panel">
|
||||||
|
<h3>Фундамент v1 <span class="tag now">В РАБОТЕ</span></h3>
|
||||||
|
<p class="meta">Скелет сервера и связь с КОМПАС</p>
|
||||||
|
<ul class="list">
|
||||||
|
<li class="muted"><span class="mark ok">✓</span><span class="t">Спайк S1: interop-сборки собираются под <code>net8.0-windows</code></span></li>
|
||||||
|
<li class="muted"><span class="mark ok">✓</span><span class="t">Спайк S2: подключение к КОМПАС (launch/attach, IApplication, версия 24.0)</span></li>
|
||||||
|
<li><span class="mark wip">~</span><span>Скелет решения (git, <code>.slnx</code>, Directory.Build.props)</span></li>
|
||||||
|
<li><span class="mark todo"></span><span>Маппинг перечислений (enum ↔ строка)</span></li>
|
||||||
|
<li><span class="mark todo"></span><span>STA-поток + диспетчер + насос сообщений</span></li>
|
||||||
|
<li><span class="mark todo"></span><span>MCP-хост на stdio, логи в stderr</span></li>
|
||||||
|
<li><span class="mark todo"></span><span>Трансляция ошибок COM в MCP-ответы</span></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- TOOLS -->
|
||||||
|
<div class="panel">
|
||||||
|
<h3>Инструменты v1 <span class="tag next">ДАЛЕЕ</span></h3>
|
||||||
|
<p class="meta">Покрытие центрального цикла моделирования</p>
|
||||||
|
<ul class="list">
|
||||||
|
<li><span class="mark todo"></span><span><code>System</code>: connect · set_visible · status</span></li>
|
||||||
|
<li><span class="mark todo"></span><span><code>Documents</code>: create · open · save · close · active</span></li>
|
||||||
|
<li><span class="mark todo"></span><span><code>Sketch</code>: create · add line/circle/arc/rect · close</span></li>
|
||||||
|
<li><span class="mark todo"></span><span><code>Features</code>: extrude boss/cut · revolve · rebuild</span></li>
|
||||||
|
<li><span class="mark todo"></span><span><code>Query</code>: select_face · list_bodies · get_part_info</span></li>
|
||||||
|
<li><span class="mark todo"></span><span><code>Vision</code>: model_snapshot · set_view 👁️</span></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- QUALITY -->
|
||||||
|
<div class="panel">
|
||||||
|
<h3>Качество и поставка <span class="tag next">ДАЛЕЕ</span></h3>
|
||||||
|
<p class="meta">Тесты, сборка, интеграция с клиентом</p>
|
||||||
|
<ul class="list">
|
||||||
|
<li><span class="mark todo"></span><span>Unit-тесты чистой логики (маппинг, валидация)</span></li>
|
||||||
|
<li><span class="mark todo"></span><span>Интеграционные тесты с КОМПАС (категория Integration)</span></li>
|
||||||
|
<li><span class="mark todo"></span><span>Smoke через MCP Inspector</span></li>
|
||||||
|
<li><span class="mark todo"></span><span>Сборка <code>kompas-mcp.exe</code> (win-x64)</span></li>
|
||||||
|
<li><span class="mark todo"></span><span>Регистрация stdio-сервера в конфиге клиента</span></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ROADMAP -->
|
||||||
|
<section id="roadmap">
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="eyebrow">Дорожная карта</div>
|
||||||
|
<h2>После v1</h2>
|
||||||
|
<p class="sub">Наращиваем покрытие API волнами — от простых операций к параметрике,
|
||||||
|
черчению и сборкам.</p>
|
||||||
|
<div class="timeline">
|
||||||
|
<div class="tl cur"><div class="ph">v1 · сейчас</div><h4>Цикл моделирования</h4><p>Документы, эскизы, выдавливание/вырез, вращение, выборка граней</p></div>
|
||||||
|
<div class="tl"><div class="ph">v2</div><h4>Операции</h4><p>Скругление, фаска, оболочка, ребро, лофт, кинематика, массивы</p></div>
|
||||||
|
<div class="tl"><div class="ph">v3</div><h4>Параметрика</h4><p>Переменные <code>IVariable7</code>, свойства <code>IPropertyMng</code>, формулы</p></div>
|
||||||
|
<div class="tl"><div class="ph">v4</div><h4>2D-черчение</h4><p>Виды, размеры, тексты, штриховки; спецификации</p></div>
|
||||||
|
<div class="tl"><div class="ph">v5</div><h4>Сборки и экспорт</h4><p>Компоненты, сопряжения, конвертеры (STEP), HTTP/SSE-транспорт</p></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<div class="wrap">
|
||||||
|
<div>КОМПАС-3D MCP · документация проекта</div>
|
||||||
|
<div class="links">
|
||||||
|
<a href="ARCHITECTURE.md">📐 Документ архитектуры</a>
|
||||||
|
<a href="IMPLEMENTATION_PLAN.md">🛠️ План реализации</a>
|
||||||
|
<a href="../CLAUDE.md">📘 CLAUDE.md</a>
|
||||||
|
<a href="KOMPAS_SDK_ru-RU/index.html">📚 Справка SDK КОМПАС-3D</a>
|
||||||
|
<a href="https://help.ascon.ru/KOMPAS_SDK/22/ru-RU/index.html">🌐 Онлайн-справка АСКОН</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,33 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<AssemblyName>kompas-connect-spike</AssemblyName>
|
||||||
|
<RootNamespace>Kompas.Mcp.ConnectSpike</RootNamespace>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!-- Вендорские interop-сборки КОМПАС (из SDK Samples/Common). -->
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="KompasAPI7">
|
||||||
|
<HintPath>$(KompasInteropDir)\KompasAPI7.dll</HintPath>
|
||||||
|
<Private>true</Private>
|
||||||
|
<EmbedInteropTypes>false</EmbedInteropTypes>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Kompas6API5">
|
||||||
|
<HintPath>$(KompasInteropDir)\Kompas6API5.dll</HintPath>
|
||||||
|
<Private>true</Private>
|
||||||
|
<EmbedInteropTypes>false</EmbedInteropTypes>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Kompas6Constants">
|
||||||
|
<HintPath>$(KompasInteropDir)\Kompas6Constants.dll</HintPath>
|
||||||
|
<Private>true</Private>
|
||||||
|
<EmbedInteropTypes>false</EmbedInteropTypes>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Kompas6Constants3D">
|
||||||
|
<HintPath>$(KompasInteropDir)\Kompas6Constants3D.dll</HintPath>
|
||||||
|
<Private>true</Private>
|
||||||
|
<EmbedInteropTypes>false</EmbedInteropTypes>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
// Спайк S1+S2: проверка interop-сборок и подключения к КОМПАС-3D.
|
||||||
|
//
|
||||||
|
// Доказывает, что:
|
||||||
|
// * вендорские interop-сборки (KompasAPI7 / Kompas6API5) собираются и загружаются под net8.0-windows;
|
||||||
|
// * клиент умеет attach к запущенному КОМПАС и launch нового экземпляра;
|
||||||
|
// * получает IApplication (API7), делает окно видимым и читает версию.
|
||||||
|
//
|
||||||
|
// Внимание: Marshal.GetActiveObject отсутствует в .NET 5+, поэтому attach делается
|
||||||
|
// через нативные CLSIDFromProgID + GetActiveObject (oleaut32).
|
||||||
|
//
|
||||||
|
// Логи -> stderr, итог -> stdout. Код возврата 0 = успех.
|
||||||
|
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
using Kompas6API5;
|
||||||
|
using KompasAPI7;
|
||||||
|
|
||||||
|
namespace Kompas.Mcp.ConnectSpike;
|
||||||
|
|
||||||
|
[SupportedOSPlatform("windows")]
|
||||||
|
internal static class Program
|
||||||
|
{
|
||||||
|
// Порядок попыток подключения. На v24 Home проверено: есть .7 и .5, нет LT.
|
||||||
|
private static readonly string[] ProgIds =
|
||||||
|
{
|
||||||
|
"KOMPAS.Application.7", // прямой API7
|
||||||
|
"KOMPAS.Application.5", // API5 -> ksGetApplication7()
|
||||||
|
"KOMPASLT.Application.5", // запасной (LT-инсталляции)
|
||||||
|
};
|
||||||
|
|
||||||
|
private static int Main()
|
||||||
|
{
|
||||||
|
Log("=== KOMPAS connect spike ===");
|
||||||
|
bool launchedByUs = false;
|
||||||
|
IApplication? app = null;
|
||||||
|
|
||||||
|
// 1) attach к уже запущенному экземпляру
|
||||||
|
foreach (var id in ProgIds)
|
||||||
|
{
|
||||||
|
var root = AttachByProgId(id);
|
||||||
|
if (root is null) continue;
|
||||||
|
app = AsApplication(root);
|
||||||
|
if (app != null) { Log($"attached: {id}"); break; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) запустить новый экземпляр
|
||||||
|
if (app is null)
|
||||||
|
{
|
||||||
|
foreach (var id in ProgIds)
|
||||||
|
{
|
||||||
|
var root = CreateByProgId(id);
|
||||||
|
if (root is null) continue;
|
||||||
|
app = AsApplication(root);
|
||||||
|
if (app != null) { launchedByUs = true; Log($"launched: {id}"); break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app is null)
|
||||||
|
{
|
||||||
|
Log("РЕЗУЛЬТАТ: подключиться к КОМПАС не удалось.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
app.Visible = true;
|
||||||
|
Console.WriteLine("CONNECTED");
|
||||||
|
ReportVersion(app);
|
||||||
|
|
||||||
|
Log(launchedByUs
|
||||||
|
? "Экземпляр запущен этим процессом (в реальном сервере здесь решаем про Quit при выходе)."
|
||||||
|
: "Подключились к уже запущенному экземпляру (Quit не вызываем).");
|
||||||
|
Log("Готово. Окно КОМПАС должно быть видимым.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Привести COM-корень к IApplication: API7 — прямой каст, API5 — через ksGetApplication7().</summary>
|
||||||
|
private static IApplication? AsApplication(object root)
|
||||||
|
{
|
||||||
|
if (root is IApplication direct)
|
||||||
|
return direct;
|
||||||
|
|
||||||
|
if (root is KompasObject ko)
|
||||||
|
{
|
||||||
|
try { return ko.ksGetApplication7() as IApplication; }
|
||||||
|
catch (Exception ex) { Log($"ksGetApplication7 failed: {ex.Message}"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
try { return (IApplication)root; }
|
||||||
|
catch { return null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ReportVersion(IApplication app)
|
||||||
|
{
|
||||||
|
try { Console.WriteLine($"BuildNumber: {app.BuildNumber}"); }
|
||||||
|
catch (Exception ex) { Log($"BuildNumber failed: {ex.Message}"); }
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int major = 0, minor = 0, release = 0, build = 0;
|
||||||
|
app.GetSystemVersion(out major, out minor, out release, out build);
|
||||||
|
Console.WriteLine($"Version: {major}.{minor}.{release} (build {build})");
|
||||||
|
}
|
||||||
|
catch (Exception ex) { Log($"GetSystemVersion failed: {ex.Message}"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object? AttachByProgId(string progId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
CLSIDFromProgID(progId, out Guid clsid);
|
||||||
|
GetActiveObject(ref clsid, IntPtr.Zero, out object obj);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null; // не запущен / не зарегистрирован
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object? CreateByProgId(string progId)
|
||||||
|
{
|
||||||
|
var type = Type.GetTypeFromProgID(progId, throwOnError: false);
|
||||||
|
if (type is null) return null;
|
||||||
|
try { return Activator.CreateInstance(type); }
|
||||||
|
catch (Exception ex) { Log($"CreateInstance({progId}) failed: {ex.Message}"); return null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("ole32.dll", PreserveSig = false)]
|
||||||
|
private static extern void CLSIDFromProgID(
|
||||||
|
[MarshalAs(UnmanagedType.LPWStr)] string progId, out Guid clsid);
|
||||||
|
|
||||||
|
[DllImport("oleaut32.dll", PreserveSig = false)]
|
||||||
|
private static extern void GetActiveObject(
|
||||||
|
ref Guid rclsid, IntPtr pvReserved,
|
||||||
|
[MarshalAs(UnmanagedType.IUnknown)] out object ppunk);
|
||||||
|
|
||||||
|
private static void Log(string msg) => Console.Error.WriteLine(msg);
|
||||||
|
}
|
||||||
+200
@@ -0,0 +1,200 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
kdoc — навигация по справке SDK КОМПАС-3D (Help & Manual WebHelp) для агента.
|
||||||
|
|
||||||
|
Справка — это ~26 000 HTML-страниц в docs/KOMPAS_SDK_ru-RU/ со «зашумлённой»
|
||||||
|
вёрсткой (шапка, подвал, JS). Листать их напрямую неэффективно. Этот инструмент:
|
||||||
|
|
||||||
|
* build-index — один раз строит компактный индекс docs/kompas_sdk_index.tsv
|
||||||
|
(файл<TAB>заголовок<TAB>краткое описание) из zoom_pageinfo.js;
|
||||||
|
* search — ищет страницы по подстроке в заголовке/описании;
|
||||||
|
* read — печатает чистый текст одной страницы (тело топика без вёрстки).
|
||||||
|
|
||||||
|
Типичный сценарий агента:
|
||||||
|
python tools/kdoc.py search "выдавливание" # найти нужный интерфейс
|
||||||
|
python tools/kdoc.py read ibossextrusiondefinition.html
|
||||||
|
|
||||||
|
Поиск по индексу можно вести и встроенным Grep по docs/kompas_sdk_index.tsv —
|
||||||
|
формат строки: <имя_файла>\t<заголовок>\t<описание>.
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import json
|
||||||
|
import html
|
||||||
|
import argparse
|
||||||
|
from html.parser import HTMLParser
|
||||||
|
|
||||||
|
# На Windows консоль часто cp1251 — принудительно выводим UTF-8,
|
||||||
|
# чтобы кириллица не искажалась при вызове из агента/любого шелла.
|
||||||
|
for _stream in (sys.stdout, sys.stderr):
|
||||||
|
try:
|
||||||
|
_stream.reconfigure(encoding="utf-8")
|
||||||
|
except (AttributeError, ValueError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
DOC_DIR = os.environ.get(
|
||||||
|
"KDOC_DIR", os.path.join(HERE, "..", "docs", "KOMPAS_SDK_ru-RU")
|
||||||
|
)
|
||||||
|
DOC_DIR = os.path.abspath(DOC_DIR)
|
||||||
|
INDEX_PATH = os.path.abspath(os.path.join(DOC_DIR, "..", "kompas_sdk_index.tsv"))
|
||||||
|
PAGEINFO = os.path.join(DOC_DIR, "zoom_pageinfo.js")
|
||||||
|
|
||||||
|
# Текстовый «мусор» навигации/подвала, попадающий в тело топика.
|
||||||
|
NOISE = {
|
||||||
|
"please enable javascript to view this site.",
|
||||||
|
"scroll", "предыдущий", "вверх", "следующий",
|
||||||
|
"содержание", "содержане", "указатель", "поиск",
|
||||||
|
"подразделы:", "(отсутствуют)",
|
||||||
|
}
|
||||||
|
FOOTER_RE = re.compile(r"©?\s*ООО\s*«?АСКОН")
|
||||||
|
|
||||||
|
|
||||||
|
def build_index() -> int:
|
||||||
|
"""Разобрать массив pagedata из zoom_pageinfo.js в TSV-индекс."""
|
||||||
|
if not os.path.isfile(PAGEINFO):
|
||||||
|
sys.exit(f"Не найден {PAGEINFO}. Проверьте KDOC_DIR / путь к справке.")
|
||||||
|
with open(PAGEINFO, "r", encoding="utf-8", errors="replace") as f:
|
||||||
|
text = f.read()
|
||||||
|
marker = "pagedata = "
|
||||||
|
pos = text.find(marker)
|
||||||
|
if pos < 0:
|
||||||
|
sys.exit("В zoom_pageinfo.js не найден массив pagedata.")
|
||||||
|
payload = text[pos + len(marker):].strip()
|
||||||
|
payload = payload.rstrip(";").strip()
|
||||||
|
data = json.loads(payload) # [["./file.html","title","desc","img"], ...]
|
||||||
|
|
||||||
|
rows = []
|
||||||
|
for entry in data:
|
||||||
|
if not entry or not entry[0]:
|
||||||
|
continue
|
||||||
|
fname = entry[0].lstrip("./").strip()
|
||||||
|
title = html.unescape((entry[1] or "").strip())
|
||||||
|
desc = html.unescape((entry[2] or "").strip())
|
||||||
|
title = re.sub(r"\s+", " ", title)
|
||||||
|
desc = re.sub(r"\s+", " ", desc)[:300]
|
||||||
|
rows.append(f"{fname}\t{title}\t{desc}")
|
||||||
|
|
||||||
|
with open(INDEX_PATH, "w", encoding="utf-8", newline="\n") as f:
|
||||||
|
f.write("\n".join(rows) + "\n")
|
||||||
|
print(f"Индекс записан: {INDEX_PATH} ({len(rows)} страниц)")
|
||||||
|
return len(rows)
|
||||||
|
|
||||||
|
|
||||||
|
def search(terms, limit=40):
|
||||||
|
"""Грубый поиск по индексу: AND по всем подстрокам, без регистра."""
|
||||||
|
if not os.path.isfile(INDEX_PATH):
|
||||||
|
print("Индекс отсутствует — строю...", file=sys.stderr)
|
||||||
|
build_index()
|
||||||
|
needles = [t.lower() for t in terms if t.strip()]
|
||||||
|
hits = 0
|
||||||
|
with open(INDEX_PATH, "r", encoding="utf-8") as f:
|
||||||
|
for line in f:
|
||||||
|
low = line.lower()
|
||||||
|
if all(n in low for n in needles):
|
||||||
|
parts = line.rstrip("\n").split("\t")
|
||||||
|
fname = parts[0]
|
||||||
|
title = parts[1] if len(parts) > 1 else ""
|
||||||
|
print(f"{fname}\t{title}")
|
||||||
|
hits += 1
|
||||||
|
if hits >= limit:
|
||||||
|
print(f"... (показаны первые {limit}; уточните запрос)")
|
||||||
|
break
|
||||||
|
if hits == 0:
|
||||||
|
print("Ничего не найдено.")
|
||||||
|
|
||||||
|
|
||||||
|
class _TopicExtractor(HTMLParser):
|
||||||
|
"""Собирает текст из <div id='topicbody'>, пропуская script/style."""
|
||||||
|
BLOCK = {"p", "div", "br", "tr", "li", "h1", "h2", "h3", "h4",
|
||||||
|
"table", "pre"}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__(convert_charrefs=True)
|
||||||
|
self.depth = 0 # глубина вложенности внутри topicbody
|
||||||
|
self.capturing = False
|
||||||
|
self.skip = 0 # внутри script/style
|
||||||
|
self.out = []
|
||||||
|
|
||||||
|
def handle_starttag(self, tag, attrs):
|
||||||
|
attrd = dict(attrs)
|
||||||
|
if not self.capturing and attrd.get("id") == "topicbody":
|
||||||
|
self.capturing = True
|
||||||
|
self.depth = 1
|
||||||
|
return
|
||||||
|
if not self.capturing:
|
||||||
|
return
|
||||||
|
if tag in ("script", "style"):
|
||||||
|
self.skip += 1
|
||||||
|
if tag in self.BLOCK:
|
||||||
|
self.out.append("\n")
|
||||||
|
if tag != "br":
|
||||||
|
self.depth += 1
|
||||||
|
|
||||||
|
def handle_endtag(self, tag):
|
||||||
|
if not self.capturing:
|
||||||
|
return
|
||||||
|
if tag in ("script", "style") and self.skip:
|
||||||
|
self.skip -= 1
|
||||||
|
if tag != "br":
|
||||||
|
self.depth -= 1
|
||||||
|
if self.depth <= 0:
|
||||||
|
self.capturing = False
|
||||||
|
|
||||||
|
def handle_data(self, data):
|
||||||
|
if self.capturing and not self.skip:
|
||||||
|
self.out.append(data)
|
||||||
|
|
||||||
|
|
||||||
|
def read(fname):
|
||||||
|
path = os.path.join(DOC_DIR, os.path.basename(fname))
|
||||||
|
if not path.endswith(".html"):
|
||||||
|
path += ".html"
|
||||||
|
if not os.path.isfile(path):
|
||||||
|
sys.exit(f"Нет файла: {path}")
|
||||||
|
with open(path, "r", encoding="utf-8", errors="replace") as f:
|
||||||
|
raw = f.read()
|
||||||
|
p = _TopicExtractor()
|
||||||
|
p.feed(raw)
|
||||||
|
text = "".join(p.out)
|
||||||
|
|
||||||
|
cleaned = []
|
||||||
|
for line in text.splitlines():
|
||||||
|
s = re.sub(r"[ \t ]+", " ", line).strip()
|
||||||
|
if not s:
|
||||||
|
continue
|
||||||
|
if s.lower() in NOISE:
|
||||||
|
continue
|
||||||
|
if FOOTER_RE.search(s):
|
||||||
|
break # подвал — дальше только копирайт/телефон
|
||||||
|
cleaned.append(s)
|
||||||
|
print(os.path.basename(path))
|
||||||
|
print("=" * len(os.path.basename(path)))
|
||||||
|
print("\n".join(cleaned).strip())
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ap = argparse.ArgumentParser(
|
||||||
|
prog="kdoc", description="Навигация по справке SDK КОМПАС-3D для агента."
|
||||||
|
)
|
||||||
|
sub = ap.add_subparsers(dest="cmd", required=True)
|
||||||
|
sub.add_parser("build-index", help="построить docs/kompas_sdk_index.tsv")
|
||||||
|
sp = sub.add_parser("search", help="искать по заголовку/описанию")
|
||||||
|
sp.add_argument("terms", nargs="+")
|
||||||
|
sp.add_argument("--limit", type=int, default=40)
|
||||||
|
rp = sub.add_parser("read", help="печать чистого текста страницы")
|
||||||
|
rp.add_argument("file")
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
if args.cmd == "build-index":
|
||||||
|
build_index()
|
||||||
|
elif args.cmd == "search":
|
||||||
|
search(args.terms, args.limit)
|
||||||
|
elif args.cmd == "read":
|
||||||
|
read(args.file)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user