Files
kompas3d-mcp/src/Kompas.Mcp.Core/Conversion/ConversionService.cs
T
mikhail 31b933e1cc Ответ надписи — габарит глифов; запись на диск создаёт каталог
ksConvertTextToCurve ВОЗВРАЩАЕТ ссылку на кривые, и только по ней ksGetObjGabaritRect
даёт габарит букв: по текстовой ссылке это ячейка строки (Arial h=10 «HH» — 23.75 против
19.97 у глифов, второе совпадает с выдавленным телом). Агент вписывает надпись в поле
по габариту, поэтому в ответе теперь оба числа.

OutputPath.Prepare создаёт каталог назначения и делает путь абсолютным (document_save,
export_step, снимок): КОМПАС в несуществующую папку молча не пишет, а диагностика при
этом винила занятое имя файла — чинили не ту причину. Причины разведены.
2026-07-31 16:36:01 +03:00

137 lines
6.9 KiB
C#

using System.Runtime.Versioning;
using Kompas.Mcp.Core.Documents;
using Kompas.Mcp.Core.Interop;
using Kompas.Mcp.Core.Threading;
using Kompas6Constants;
using KompasAPI7;
namespace Kompas.Mcp.Core.Conversion;
/// <summary>
/// Импорт/экспорт обменных форматов (STEP) через КОМПАС-конвертер.
/// <para>
/// Встроенный конвертер получается через <c>IApplication.get_Converter(код_формата)</c>
/// (целочисленный код <c>ksKOMPASConverterEnum</c>, НЕ путь к DLL), параметры — через
/// <c>IConverter.ConverterParameters</c>, сама конвертация — через
/// <c>IKompasDocument3D1.ConvertTo/FromAdditionFormat</c>.
/// </para>
/// </summary>
[SupportedOSPlatform("windows")]
public sealed class ConversionService
{
private readonly KompasSession _session;
private readonly KompasDispatcher _dispatcher;
public ConversionService(KompasSession session, KompasDispatcher dispatcher)
{
_session = session ?? throw new ArgumentNullException(nameof(session));
_dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
}
/// <summary>
/// Импортировать STEP-файл в новый документ заданного типа (по умолчанию — сборка) и сделать его активным.
/// <paramref name="createComponentFiles"/> — писать ли компоненты сборки отдельными .m3d рядом с STEP.
/// </summary>
public Task ImportStepAsync(
string path,
KompasDocumentType docType = KompasDocumentType.Assembly,
bool createComponentFiles = false,
CancellationToken ct = default)
=> _dispatcher.InvokeAsync(() => ImportCore(path, docType, createComponentFiles), ct);
private void ImportCore(string path, KompasDocumentType docType, bool createComponentFiles)
{
if (!File.Exists(path))
throw new FileNotFoundException($"STEP-файл не найден: {path}", path);
var app = _session.Application;
var typeEnum = DocumentTypes.ToEnum(docType);
IConverter? conv = null;
IAdditionConvertParameters? prm = null;
object? doc = null;
try
{
conv = GetConverter(app, StepFormats.ImportConverterCode); // -3
conv.GetFilter((int)typeEnum, false /* импорт */, out int cmd);
doc = app.Documents.Add(typeEnum, true);
if (doc is not IKompasDocument3D1 doc3d1)
throw new InvalidOperationException("Созданный документ не является 3D-документом (нет IKompasDocument3D1).");
prm = conv.ConverterParameters(cmd) as IAdditionConvertParameters
?? throw new InvalidOperationException("ConverterParameters вернул не IAdditionConvertParameters.");
prm.Format = (ksKOMPASConverterEnum)StepFormats.ImportConverterCode;
if (createComponentFiles)
{
// NeedCreateComponentsFiles может отсутствовать в Home-редакции КОМПАС —
// ловим COM-исключения; не критично, если свойство недоступно.
try { prm.NeedCreateComponentsFiles = true; }
catch (System.Runtime.InteropServices.COMException) { }
catch (System.Runtime.InteropServices.InvalidComObjectException) { }
catch (NotImplementedException) { }
}
if (!doc3d1.ConvertFromAdditionFormat(path, (AdditionConvertParameters)prm))
throw new InvalidOperationException("ConvertFromAdditionFormat вернул FALSE (импорт STEP не удался).");
}
finally
{
ComHelper.Release(prm);
ComHelper.Release(doc);
ComHelper.Release(conv);
}
}
/// <summary>Экспортировать активный 3D-документ в STEP по указанному пути.</summary>
public Task ExportStepAsync(string path, StepFormat format = StepFormat.Auto, CancellationToken ct = default)
=> _dispatcher.InvokeAsync(() => ExportCore(path, format), ct);
private void ExportCore(string path, StepFormat format)
{
// Каталог создаём сами: конвертер в несуществующую папку возвращает FALSE без причины.
var full = Documents.OutputPath.Prepare(path);
var app = _session.Application;
object? active = null;
IConverter? conv = null;
IAdditionConvertParameters? prm = null;
try
{
active = app.ActiveDocument
?? throw new InvalidOperationException("Нет активного документа для экспорта.");
if (active is not IKompasDocument3D1 doc3d1)
throw new InvalidOperationException("Активный документ не является 3D-документом (нет IKompasDocument3D1).");
int formatCode = StepFormats.ExportConverterCode(format);
conv = GetConverter(app, formatCode);
conv.GetFilter((int)DocumentTypeEnum.ksDocumentPart, true /* saveAs = экспорт */, out int cmd);
prm = conv.ConverterParameters(cmd) as IAdditionConvertParameters
?? throw new InvalidOperationException("ConverterParameters вернул не IAdditionConvertParameters.");
prm.Format = (ksKOMPASConverterEnum)formatCode;
if (!doc3d1.ConvertToAdditionFormat(full, (AdditionConvertParameters)prm))
throw new InvalidOperationException($"ConvertToAdditionFormat вернул FALSE (экспорт STEP по пути «{full}» не удался).");
}
finally
{
ComHelper.Release(prm);
ComHelper.Release(active);
ComHelper.Release(conv);
}
}
/// <summary>
/// Получить встроенный конвертер. Для STEP get_Converter принимает целочисленный код формата
/// (boxed int), НЕ путь к DLL. Пробуем код формата, затем базовый код экспорта STEP (3).
/// </summary>
private static IConverter GetConverter(IApplication app, int formatCode)
{
if (app.Converter[(object)formatCode] is IConverter c) return c;
// AP-вариант мог не быть отдельным конвертером — берём базовый STEP-экспорт (ksConverterToSTEP = 3).
if (app.Converter[(object)3] is IConverter cBase) return cBase;
throw new InvalidOperationException(
$"get_Converter({formatCode}) вернул null — STEP-конвертер недоступен в этой редакции КОМПАС.");
}
}