Files
kompas3d-mcp/src/Kompas.Mcp.Host/Tools/QueryTools.cs
T
mikhail 28d2ae3868 feat(inspection): структурное «зрение» модели — осмотр без снимка
Новый слой «модель как данные» (приоритетный над model_snapshot):
- describe_model — структурный «паспорт» детали одним вызовом (габарит,
  МЦХ, тела, сводка топологии, дерево построения с параметрами, переменные)
- list_features / list_bodies / list_variables
- describe_face / describe_edge (drill-down по индексу)
- measure (расстояние/угол между гранями/рёбрами/вершинами)
- model_snapshot понижен до fallback для визуально-пространственных вопросов

Реализация: ModelInspectionService (COM) + чистый InspectionText (рендер,
группировка, детект «импорт без истории») + record-модели; InspectionTools.
Дерево читается через ksPart.GetFeature()->SubFeatureCollection; точный тип
и параметры операций берутся из GetObject()->GetDefinition() (ksFeature.type
отдаёт лишь coarse o3d_entity).

Снимок дерева также включает слои conversion (STEP), editing (move_face/solid)
и validation, развивавшиеся параллельно. Полный прогон: 61 тест зелёный,
43 MCP-инструмента. Документация (README, ARCHITECTURE, OPEN_QUESTIONS,
CLAUDE.md, presentation.html) синхронизирована.

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

70 lines
4.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.ComponentModel;
using Kompas.Mcp.Core;
using Kompas.Mcp.Core.Query;
using ModelContextProtocol.Server;
namespace Kompas.Mcp.Host.Tools;
/// <summary>Инструменты запросов к модели.</summary>
[McpServerToolType]
public sealed class QueryTools(KompasSession session, QueryService query)
{
[McpServerTool(Name = "get_part_info")]
[Description("Массово-центровочные характеристики активной детали: объём (мм³), масса (кг), площадь (мм²) и центр масс. Полезно для числовой проверки построения.")]
public async Task<string> GetPartInfo()
{
await session.ConnectAsync();
var i = await query.GetPartInfoAsync();
return $"Объём: {i.Volume:F2} мм³; масса: {i.Mass:F4} кг; площадь: {i.Area:F2} мм²; " +
$"центр масс: ({i.CenterX:F2}, {i.CenterY:F2}, {i.CenterZ:F2}).";
}
[McpServerTool(Name = "list_components")]
[Description("Перечислить компоненты верхнего уровня активной сборки: индекс, имя, обозначение, признак детали и габарит (мм). Индекс/имя — для последующего извлечения или осмотра детали.")]
public async Task<string> ListComponents()
{
await session.ConnectAsync();
var comps = await query.ListComponentsAsync();
if (comps.Count == 0) return "Компонентов нет (активный документ не сборка или пуст).";
var lines = comps.Select(c =>
{
var mark = string.IsNullOrWhiteSpace(c.Marking) ? "" : $", обозн. \"{c.Marking}\"";
return $" [{c.Index}] {(c.IsDetail ? "деталь" : "узел ")} \"{c.Name}\", " +
$"габ {c.SizeX:F1}×{c.SizeY:F1}×{c.SizeZ:F1} мм{mark}";
});
return $"Компонентов: {comps.Count}\n" + string.Join("\n", lines);
}
[McpServerTool(Name = "list_faces")]
[Description("Перечислить грани активной детали: индекс, тип поверхности (plane/cylinder/cone/sphere/torus/nurbs) и площадь (мм²). Индекс используйте в sketch_create_on_face_index. Удобно для надёжного выбора грани без угадывания координат.")]
public async Task<string> ListFaces()
{
await session.ConnectAsync();
var faces = await query.ListFacesAsync();
if (faces.Count == 0) return "Граней нет (модель пуста?).";
var lines = faces.Select(f => $" [{f.Index}] {f.Type}, площадь {f.Area:F2} мм²");
return $"Граней: {faces.Count}\n" + string.Join("\n", lines);
}
[McpServerTool(Name = "list_edges")]
[Description("Перечислить рёбра активной детали: индекс, тип кривой (line/circle/arc/ellipse/nurbs) и длину (мм). Индекс используйте в fillet_edge_index / chamfer_edge_index для надёжного выбора ребра без угадывания координат.")]
public async Task<string> ListEdges()
{
await session.ConnectAsync();
var edges = await query.ListEdgesAsync();
if (edges.Count == 0) return "Рёбер нет (модель пуста?).";
var lines = edges.Select(e => $" [{e.Index}] {e.Type}, длина {e.Length:F2} мм");
return $"Рёбер: {edges.Count}\n" + string.Join("\n", lines);
}
[McpServerTool(Name = "get_bounding_box")]
[Description("Габариты активной детали: координаты габаритного параллелепипеда (мм) и его размеры. Помогает оценить протяжённость модели и подобрать точки для выбора граней/рёбер.")]
public async Task<string> GetBoundingBox()
{
await session.ConnectAsync();
var b = await query.GetBoundingBoxAsync();
return $"Габарит: X [{b.MinX:F2}; {b.MaxX:F2}], Y [{b.MinY:F2}; {b.MaxY:F2}], Z [{b.MinZ:F2}; {b.MaxZ:F2}]; " +
$"размеры (Д×Ш×В): {b.SizeX:F2} × {b.SizeY:F2} × {b.SizeZ:F2} мм.";
}
}