using System.Globalization; using System.Runtime.Versioning; using Kompas.Mcp.Core.Threading; using Kompas6API5; using Kompas6Constants3D; namespace Kompas.Mcp.Core.Modeling; /// /// Параметрика: создание и изменение переменных модели (API5 ksVariable). /// Создание возможно только на коллекции переменных корневого ksFeature (не ksPart). /// ВАЖНО: переменная управляет геометрией лишь в параметрической модели (где размеры /// эскизов связаны с именованными переменными). Эскизы, построенные литеральными координатами, /// не имеют именованных размеров — для них переменные хранятся, но геометрию не меняют. /// [SupportedOSPlatform("windows")] public sealed class VariableService { private readonly KompasSession _session; private readonly KompasDispatcher _dispatcher; public VariableService(KompasSession session, KompasDispatcher dispatcher) { _session = session ?? throw new ArgumentNullException(nameof(session)); _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher)); } /// Создать новую переменную модели с начальным значением. external=true — внешняя /// (доступна для управления из сборки). Возвращает вычисленное значение. public Task CreateVariableAsync(string name, double value, string? note = null, bool external = false, CancellationToken ct = default) => _dispatcher.InvokeAsync(() => { RequireName(name); if (!double.IsFinite(value)) throw new ArgumentException("Значение должно быть конечным.", nameof(value)); var (part, vc) = RootVariables(); if (vc.GetByName(name, true, false) is ksVariable) throw new InvalidOperationException($"Переменная '{name}' уже существует."); var v = vc.AddNewVariable(name, value, note ?? "") as ksVariable ?? throw new InvalidOperationException("AddNewVariable вернул null (недопустимое имя?)."); if (external) v.external = true; part.RebuildModel(); return ReadValueFresh(name); // значение перечитываем после перестроения }, ct); /// Задать выражение переменной (константа «30» или формула «len*2+5») и перестроить. /// Возвращает вычисленное значение. public Task SetVariableAsync(string name, string expression, CancellationToken ct = default) => _dispatcher.InvokeAsync(() => { RequireName(name); if (string.IsNullOrWhiteSpace(expression)) throw new ArgumentException("Выражение не может быть пустым.", nameof(expression)); var (part, vc) = RootVariables(); var v = RequireVariable(vc, name); v.Expression = expression; part.RebuildModel(); return ReadValueFresh(name); // переменную перечитываем после перестроения (value пересчитан) }, ct); /// Удалить переменную модели. public Task DeleteVariableAsync(string name, CancellationToken ct = default) => _dispatcher.InvokeAsync(() => { RequireName(name); var (part, vc) = RootVariables(); RequireVariable(vc, name); // понятная ошибка, если её нет if (!vc.RemoveVariable(name)) throw new InvalidOperationException($"Не удалось удалить переменную '{name}'."); part.RebuildModel(); }, ct); /// Прочитать текущее (вычисленное) значение переменной. public Task GetVariableValueAsync(string name, CancellationToken ct = default) => _dispatcher.InvokeAsync(() => { RequireName(name); var (_, vc) = RootVariables(); return RequireVariable(vc, name).value; }, ct); /// Перечитать значение переменной из свежей коллекции (после RebuildModel оно пересчитано). private double ReadValueFresh(string name) { var (_, vc) = RootVariables(); return RequireVariable(vc, name).value; } private static void RequireName(string name) { if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Имя переменной не может быть пустым.", nameof(name)); } private static ksVariable RequireVariable(ksVariableCollection vc, string name) => vc.GetByName(name, true, false) as ksVariable ?? throw new KeyNotFoundException($"Переменная '{name}' не найдена."); /// Корневой ksFeature детали и его коллекция переменных (на ней работает AddNewVariable). private (ksPart part, ksVariableCollection vc) RootVariables() { var doc3d = _session.Kompas.ActiveDocument3D() as ksDocument3D ?? throw new InvalidOperationException("Нет активного 3D-документа."); var part = doc3d.GetPart((short)Part_Type.pTop_Part) as ksPart ?? throw new InvalidOperationException("Не удалось получить вершинный компонент (ksPart)."); var feature = part.GetFeature() as ksFeature ?? throw new InvalidOperationException("GetFeature() вернул null (нет дерева построения)."); var vc = feature.VariableCollection as ksVariableCollection ?? throw new InvalidOperationException("VariableCollection вернул не ksVariableCollection."); return (part, vc); } }