Что пришлось изменить в привычном цикле, чтобы сократить разработку одного параметрического символа с четырёх часов до одного.

Четыре часа на разработку одного параметрического клапана звучат терпимо. Пока вам не сообщают, что через месяц придёт РКД на тысячи единиц арматуры: более 100 уникальных геометрических символов и около 10 000 типоразмеров.
Даже если считать каждый символ простым, это уже 300–400 часов работы разработчика. И это нижняя граница: простыми будут далеко не все. Подготовиться заранее тоже нельзя — чертежи ещё проходят согласование. Основной объём разработки начнётся только после их получения, а срок выпуска от этого длиннее не станет.
Работы шли в рамках годового договора поддержки Smart3D. На фоне инфляции и индексации зарплат его исходная экономика становилась всё менее привлекательной. Добавить людей — значит быстрее наращивать себестоимость; ускориться за счёт сокращения проверок — рисковать качеством.
Значит, менять нужно было не численность команды, а саму единицу работы. Снизить порог входа в разработку для инженера, убрать повторяющееся программирование геометрических примитивов и при этом раньше находить ошибки.
Старый цикл выглядел так:
чертёж → ручное написание C# → компиляция DLL → загрузка в Smart3D → отладка символа: положение элементов, параметрика и поведение примитивов при смене ключевых размеров → возврат в код
За этой короткой цепочкой скрывались подбор конструкторов примитивов, описание входных переменных, расчёт координат и ориентаций, подготовка Excel-книги и повторная проверка типоразмеров.
Главная проблема была даже не в количестве строк C#: труднее убедиться, что десятки параметрических зависимостей правильно работают на разных типоразмерах. Неверное положение тела или неполная формула часто становились видны только после компиляции и загрузки DLL в Smart3D.
Нам был нужен способ собирать и проверять геометрию до компиляции, а код получать уже из проверенного описания.
Model Studio как визуальный редактор исходной геометрии
В Model Studio CS уже есть графический интерфейс для создания параметрических объектов. Инженер может добавлять примитивы и переменные, строить вытягивания и тела вращения, а в свойствах геометрии ссылаться на заданные параметры.
Model Studio в этом сценарии используется как визуальная среда подготовки, отладки и выверки исходной геометрии, а не как замена Smart3D. Инженер собирает элемент графически, задаёт формулы размеров и привязок, проверяет взаимное положение примитивов и затем экспортирует описание в XPG для преобразования в C#. Каталожные типоразмеры на этом этапе ещё не создаются.
Для испытаний использовалась Model Studio CS «Трубопроводы» на nanoCAD 24.1. Целевой системой оставалась Smart3D 2016.
Первый прототип конвертера появился примерно за неделю неспешной работы. Но довольно быстро выяснилось, что одного преобразования форматов недостаточно.
Без напильника не обошлось.
Мы начали писать конвертер и довольно быстро поняли: без традиционной доработки напильником не обойтись. Оставалось выбрать зернистость. COM и .NET позволяли работать снаружи, но для изменения поведения самого графического редактора понадобился самый грубый инструмент — расширение нижнего уровня на C++.

В штатном интерфейсе положением и ориентацией примитивов приходилось управлять в основном через таблицу параметров. Формально все нужные значения доступны. Практически, когда элемент состоит из десятка тел с разными локальными системами координат, такое редактирование превращается в перебор чисел.
Нам не хватало трёх простых графических операций:
увидеть локальные оси выбранного примитива;
повернуть его на 90° вокруг нужной оси;
зеркально отразить относительно выбранной оси.
Отдельная проблема возникала с двумерными профилями для вытягивания и вращения. Координаты точек и радиусы дуг тоже удобнее проверять непосредственно на чертеже, а не разыскивать среди строк таблицы.
Поэтому для nanoCAD/Model Studio было написано отдельное NRX-расширение на C++. Исходный код самой Model Studio мы не изменяли: плагин расширяет редактор на нативном уровне.
Теперь у выбранного элемента показываются локальные оси X, Y и Z с интерактивными маркерами. Щелчок по маркеру зеркалит элемент, средняя кнопка мыши поворачивает его на 90° вокруг соответствующей положительной оси.

Для профилей отображаются редактируемые точки и их текущие координаты. Маркер можно выбрать непосредственно на геометрии, после чего изменить X, Y или радиус дуги, в том числе задать значение формулой.

Эти вспомогательные оси и маркеры существуют только во время редактирования. Они не записываются в DWG и не попадают в XPG.
На поиск рабочего нативного маршрута ушло около 10–15 итераций, включая аварийные завершения приложения. Зато сама пользовательская доработка после исследования заняла примерно один день.
На практике главный выигрыш оказался не в самих кнопках, а в том, когда мы начали видеть ошибку. Раньше неправильная ось, зеркально развёрнутый примитив, разрыв профиля или неверная привязка проявлялись после сборки и загрузки символа в Smart3D. Теперь большинство таких проблем видно сразу в исходной геометрии.
Что находится между двумя САПР
Model Studio экспортирует параметрический объект в XPG. Формат похож на XML: в нём описаны элементы, их параметры, значения, формулы, группы, координаты и узлы подключения.
Полный рабочий маршрут показан на схеме как последовательность действий с явными входами и выходами. Среду и пакет плагина нужно настроить один раз; дальше разработчик проходит шесть шагов от чертежа до проверенных типоразмеров. Model Studio отвечает за устройство и отладку геометрии, а значения каталожных типоразмеров задаются позже в загрузочной Excel-книге.

Решение состоит из двух основных частей:
нативного C++/NRX-плагина внутри nanoCAD/Model Studio;
отдельного WPF-конвертера на C#, который читает XPG и генерирует исходный код символа Smart3D.
Плагин запускает штатный экспорт XPG, после чего файл передаётся конвертеру.

SymbolS3D запускает экспорт XPG и открывает конвертер непосредственно из Model Studio — переносить XML через буфер обмена больше не требуется. В раннем прототипе XPG вставлялся из буфера обмена; сейчас маршрут объединён и не требует ручного переноса текста.

Внутри конвертера XPG сначала превращается в собственную промежуточную модель SceneModel. В ней находятся:
примитивы и профили;
параметры и формулы;
накопленные трансформации вложенных групп;
порты;
предлагаемое имя класса.
Сегодня поддерживаются коробки, цилиндры, конусы, полные торы, сферы, вытягивания и тела вращения. Для профилей работают прямые, дуги и специальные сценарии отверстий.
Конвертер подбирает шаблон по количеству портов и их расположению относительно друг друга: End для однопортовых элементов, Inline для двухпортовых и Branch для ответвлений. Для операторов арматуры используется отдельный шаблон. Геометрический генератор при этом работает с тем же набором тел независимо от назначения элемента.
Самая неприятная часть — не XML, а системы координат.
Прочитать значения из XML несложно. Гораздо труднее добиться того, чтобы один и тот же примитив одинаково выглядел в двух геометрических ядрах.
У систем различаются соглашения о координатах, направлении осей, начальной точке тела и способе задания ориентации. Базовое преобразование координат в текущем конвертере выглядит так:
Model Studio (X, Y, Z) -> Smart3D (X, Z, -Y)
Чтобы воспроизвести каждый примитив, потребовалось найти полные конструкторы и методы конкретной версии Smart3D. Здесь пригодился локальный RAG-помощник по закрытому API из предыдущей статьи: вместо догадок он позволял искать реальные классы и проверять способы их применения.
Но одной перестановки координат недостаточно. Цилиндр, тор, вытягивание и тело вращения создаются разными конструкторами и по-разному интерпретируют направление и локальный базис. Приходится учитывать:
направление продольной оси примитива;
вектор ориентации;
начало локальной системы координат;
преобразования родительских групп;
порядок поворотов и переноса профиля;
зеркальность после перехода между правой и левой системой базисов.
На практике ошибка в одном из этих правил выглядит вполне наглядно: цилиндр уходит на другую сторону корпуса, тор разворачивается, а профиль вытягивается в неверном направлении или оказывается зеркальным. Пока один и тот же объект не увидишь в обеих системах, правильное преобразование трудно отличить от правдоподобного.
Именно сопоставление примитивов, их положения и ориентации оказалось самой трудной частью работы. Практические таблицы соответствий появились методом проб, ошибок и повторных тестов.
Зато после того, как соответствие для примитива проверено, оно начинает работать для всех следующих символов. Это и есть основной экономический эффект подхода: однажды найденное правило перестаёт оставаться в голове разработчика и становится частью конвертера.
Один клапан: от XPG до C#
В качестве сквозного примера возьмём реальный параметрический inline-клапан с приводом. В XPG он получил имя Valve_01E1.

Элемент состоит из 11 геометрических тел:
шести цилиндров;
одного полного тора;
трёх вытягиваний профиля;
одного тела вращения.
В исходном XPG объявлены переменные размеров, среди которых B, C1–C4, D, D1, FaceToFace, H, H1, H2, L, L1–L3, M, R1, R2, S, T и T2.
Имя будущего класса находится в обычном параметре XPG:
<Parameter name="Name" value="Valve_01E1" caption="Name" />
Формулы хранятся рядом с вычисленным значением. Например, высота одного из цилиндров описана так:
<Parameter name="Height" value="420.5" comment="[H]+18+16+[M]" /> <Parameter name="Radius" value="45" comment="[D]/2" />
Для конвертера это две разные части информации. value позволяет показать и проверить текущую геометрию, а comment сохраняет зависимость, которая должна работать на других типоразмерах.
Всего в этом XPG нашлось 43 атрибута с формулами. Они управляют не только высотой и радиусом, но также координатами тел, групповыми смещениями, точками профиля и положением элементов относительно друг друга.
Два узла подключения в исходном файле находятся на оси X в точках 250 и -250. При исходном межторцевом размере FaceToFace = 500 конвертер восстанавливает параметрическую зависимость, а не оставляет фиксированные координаты:
Position portPos1 = new Position(FaceToFace / 2, 0, 0); Vector portDir1 = new Vector(1, 0, 0); Position portPos2 = new Position(-FaceToFace / 2, 0, 0); Vector portDir2 = new Vector(-1, 0, 0);
Из имени объекта автоматически получается объявление класса Smart3D:
[CacheOption(CacheOptionType.Cached)] [VariableOutputs] public class Valve_01E1 : CustomSymbolDefinition { [InputCatalogPart(1)] public InputCatalogPart m_oPartDef; [InputDouble(2, "B", "B", 0.0)] public InputDouble m_dB; // Остальные входные параметры... }
А фрагмент цилиндра из XPG превращается в вызов конструктора Smart3D:
h.ActivePosition = new Position(FaceToFace / 10, 0, 0); h.SetOrientation( new Vector(0, 1, 0), new Vector(0, 0, -1)); var body = h.CreateCylinder( oConnection, D / 2, H + 0.018 + 0.016 + M);
Здесь заодно видна ещё одна обязанность генератора: числовые константы из миллиметров переводятся в метры, поэтому 18 и 16 из XPG становятся 0.018 и 0.016 в C#.
Наиболее содержательная часть примера — корпус, построенный вращением профиля. В XPG его контур состоит из прямых и параметрических дуг, часть координат связана с FaceToFace. Генератор создаёт коллекцию ICurve, рассчитывает промежуточные точки дуг, трансформирует профиль и передаёт его в Graphics3D.CreateRevolution.
Показывать весь получившийся класс в статье нет смысла: он длинный. Важнее, что разработчик его не писал вручную. Код был собран из геометрии и формул исходного объекта.
Итерации не исчезли — они стали дешевле
Экономия появилась не потому, что итерации исчезли, а потому, что большая часть отладки переместилась до компиляции.
При этом элемент не получился идеальным с первой попытки. После первой загрузки DLL и подготовки Excel-книги мы провели в Smart3D три итерации проверки разных типоразмеров. Выяснилось, что некоторые размеры нужно допараметризовать: добавить формулы, управляющие изменением размеров и взаимными привязками частей. Эти исправления вносились в исходную геометрию Model Studio, после чего C# генерировался заново.

Инженерная проверка никуда не исчезла, и недостающие зависимости конвертер не угадывает. Мы просто развели два этапа. Геометрию, ориентации и формулы отлаживаем визуально в Model Studio, а каталожные типоразмеры проверяем после загрузки в Smart3D. Если там обнаруживается недостающая зависимость, исправляем исходный объект и запускаем конвертацию заново. Сгенерированный C# не редактируем, чтобы не получить две расходящиеся версии одной геометрии — графическую и программную.
По уже выполненным задачам ориентировочный эффект выглядит так:
Сложность символа | Старый процесс | Новый процесс |
|---|---|---|
Простой | 3–4 часа | около 1 часа |
Сложный | около 8 часов | около 4 часов |
Очень сложный | 2–3 дня | около 1 дня |
Где заканчивается автоматизация.
Мы заранее ограничили задачу и не пытались сделать «универсальный конвертер всего». Автоматически переносится типовая параметрическая геометрия, для которой есть проверенное соответствие примитивов и шаблон Smart3D. Автоматизация заканчивается на сгенерированном классе: решение о детализации, компиляция, первая загрузка DLL и заполнение Excel-книги остаются частью обычного рабочего процесса.
Для каждого типа элемента уже есть шаблон книги. Добавление недостающих атрибутов занимает около пяти минут, поэтому автоматизировать этот этап пока нет практического смысла.
Нативная часть тоже имеет цену. Она зависит от конкретных версий nanoCAD и Model Studio, затрагивает внутреннее поведение редактора и требует сопровождения. Нижний уровень дал нужный UX, но бесплатной универсальности не бывает.
Текущую границу решения проще показать в таблице:
Уже работает | Пока не входит в решение |
|---|---|
Перенос параметров и поддерживаемой геометрии | Обратное преобразование Smart3D → Model Studio |
Генерация C# | Полная миграция каталогов |
Выбор шаблона элемента | Перенос размещённых экземпляров модели |
Проверка типоразмеров в Smart3D | Универсальная поддержка любых примитивов |
Немного парадоксальное импортозамещение
Получилась немного парадоксальная картина: отечественную Model Studio CS мы дорабатывали, чтобы быстрее создавать геометрию для зарубежной Smart3D. Но именно открытость отечественной платформы и возможность расширять её на разных уровнях позволили за короткий срок изменить рабочий процесс. Российский инструмент оказался не только заменой, но и основой для интеграции с уже накопленным зарубежным контуром.
На практике в этом нет большого противоречия. Многие промышленные проекты ещё долго будут жить в зарубежных САПР: в них уже накоплены модели, каталоги, правила построения и тысячи параметрических позиций. Переключить такую среду за один день невозможно — старые данные придётся поддерживать и переносить постепенно.
Проверенный маршрут можно развернуть и в обратную сторону — от зарубежной Smart3D к российской инженерной среде. Но это уже не следующая функция конвертера, а отдельный проект, который потребует инвестиций в обратный разбор символов, перенос каталогов, экземпляров, связей и метаданных.
Целевой результат такого проекта — полный перенос модели: не только геометрии отдельных символов, но и каталогов, размещённых экземпляров, координат, связей и инженерных метаданных. Текущая разработка проверяет самый нижний технический слой этого маршрута — преобразование параметрической геометрии между платформами.
В первой статье я называл инженерной памятью не только готовые модели и документы, но и правила получения результата. Здесь такими правилами стали проверенные сопоставления примитивов, координат, ориентаций и формул. Они остаются в промежуточной модели, генераторе и шаблонах, поэтому следующий символ уже не начинается с новой серии экспериментов с осями.
Практический итог проще: геометрию и параметрику теперь в основном отлаживаем до компиляции, C# собирается из проверенного описания, а найденные правила повторно используются для следующих символов. Для потока из сотни разных символов именно это важнее попытки однажды написать ещё один клапан чуть быстрее.

