31b933e1cc
ksConvertTextToCurve ВОЗВРАЩАЕТ ссылку на кривые, и только по ней ksGetObjGabaritRect даёт габарит букв: по текстовой ссылке это ячейка строки (Arial h=10 «HH» — 23.75 против 19.97 у глифов, второе совпадает с выдавленным телом). Агент вписывает надпись в поле по габариту, поэтому в ответе теперь оба числа. OutputPath.Prepare создаёт каталог назначения и делает путь абсолютным (document_save, export_step, снимок): КОМПАС в несуществующую папку молча не пишет, а диагностика при этом винила занятое имя файла — чинили не ту причину. Причины разведены.
104 lines
6.0 KiB
C#
104 lines
6.0 KiB
C#
using System.ComponentModel;
|
|
using Kompas.Mcp.Core;
|
|
using Kompas.Mcp.Core.Documents;
|
|
using Kompas.Mcp.Core.Modeling;
|
|
using ModelContextProtocol.Server;
|
|
|
|
namespace Kompas.Mcp.Host.Tools;
|
|
|
|
/// <summary>Инструменты управления документами.</summary>
|
|
[McpServerToolType]
|
|
public sealed class DocumentTools(KompasSession session, DocumentService docs, PartModeler modeler)
|
|
{
|
|
[McpServerTool(Name = "document_create")]
|
|
[Description("Создать новый документ и сделать его активным. type: part | assembly | drawing | fragment | specification | text.")]
|
|
public async Task<string> Create([Description("Тип документа")] string type)
|
|
{
|
|
await session.ConnectAsync();
|
|
var info = await docs.CreateAsync(DocumentTypes.Parse(type));
|
|
await modeler.ResetAsync(); // прежние id эскизов/операций недействительны
|
|
return $"Создан документ типа {info.Type}.";
|
|
}
|
|
|
|
[McpServerTool(Name = "document_open")]
|
|
[Description("Открыть документ КОМПАС с диска по полному пути.")]
|
|
public async Task<string> Open(
|
|
[Description("Полный путь к файлу")] string path,
|
|
[Description("Открыть только для чтения")] bool readOnly = false)
|
|
{
|
|
await session.ConnectAsync();
|
|
var info = await docs.OpenAsync(path, readOnly);
|
|
await modeler.ResetAsync();
|
|
return $"Открыт документ: {info.Name} ({info.Type}).";
|
|
}
|
|
|
|
[McpServerTool(Name = "document_save")]
|
|
[Description("Сохранить активный документ. Для нового (ещё не сохранённого) документа передайте path — " +
|
|
"полный путь с расширением: .m3d (деталь), .a3d (сборка), .cdw (чертёж), .frw (фрагмент). " +
|
|
"Без path документ сохраняется туда, откуда открыт (только для уже сохранённого ранее). " +
|
|
"path также используют, чтобы сохранить копию под новым именем.")]
|
|
public async Task<string> Save(
|
|
[Description("Полный путь файла с расширением; пусто — сохранить на прежнее место")] string? path = null)
|
|
{
|
|
await session.ConnectAsync();
|
|
if (string.IsNullOrWhiteSpace(path))
|
|
{
|
|
await docs.SaveAsync();
|
|
return "Документ сохранён.";
|
|
}
|
|
|
|
var saved = await docs.SaveAsAsync(path);
|
|
return $"Сохранено: {saved}";
|
|
}
|
|
|
|
[McpServerTool(Name = "document_close")]
|
|
[Description("Закрыть активный документ. save — сохранять ли изменения при закрытии. " +
|
|
"all=true закрывает ВСЕ открытые документы БЕЗ сохранения — этим освобождают имя файла, " +
|
|
"занятое другим открытым документом (иначе КОМПАС молча не перезапишет его).")]
|
|
public async Task<string> Close(
|
|
[Description("Сохранять изменения")] bool save = false,
|
|
[Description("Закрыть все открытые документы без сохранения")] bool all = false)
|
|
{
|
|
await session.ConnectAsync();
|
|
if (all)
|
|
{
|
|
var closed = await docs.CloseAllAsync();
|
|
await modeler.ResetAsync();
|
|
return $"Закрыто документов: {closed} (без сохранения).";
|
|
}
|
|
|
|
var ok = await docs.CloseAsync(save);
|
|
await modeler.ResetAsync();
|
|
return ok ? "Документ закрыт." : "Не удалось закрыть документ.";
|
|
}
|
|
|
|
[McpServerTool(Name = "set_part_info")]
|
|
[Description("Задать наименование и/или обозначение детали (сборки) активного 3D-документа. " +
|
|
"Наименование заменяет безликую «Деталь» в дереве построения и подставляется в штамп " +
|
|
"чертежа и в спецификацию, поэтому давайте его КАЖДОЙ детали, а не только перед выдачей. " +
|
|
"Не переданный параметр остаётся без изменений. Возвращает значения после записи.")]
|
|
public async Task<string> SetPartInfo(
|
|
[Description("Наименование детали, например «Значок декоративный»")] string? name = null,
|
|
[Description("Обозначение по ЕСКД, например «ОМЕ.741124.001»")] string? marking = null)
|
|
{
|
|
await session.ConnectAsync();
|
|
if (name is null && marking is null)
|
|
throw new ArgumentException("Передайте name и/или marking — иначе менять нечего.");
|
|
|
|
var identity = await docs.SetPartIdentityAsync(name, marking);
|
|
var markingPart = identity.Marking.Length == 0 ? "" : $", обозначение «{identity.Marking}»";
|
|
return $"Деталь: наименование «{identity.Name}»{markingPart}.";
|
|
}
|
|
|
|
[McpServerTool(Name = "document_active")]
|
|
[Description("Сведения об активном документе.")]
|
|
public async Task<string> Active()
|
|
{
|
|
await session.ConnectAsync();
|
|
var i = await docs.GetActiveAsync();
|
|
if (i is null) return "Нет активного документа.";
|
|
var where = string.IsNullOrEmpty(i.PathName) ? "не сохранён" : i.PathName;
|
|
return $"{i.Type}: {i.Name} ({where}).";
|
|
}
|
|
}
|