Введение
Привет, Хабр!
Проектирование конструкторской документации для систем автоматизации в EPLAN ELECTRIC P8 — это интересный и многогранный процесс. Обычно он включает:
разработку электрических принципиальных схем шкафов;
компоновку оборудования в 3D;
оформление чертежей и генерацию отчётов.
На каждом этапе стандартные средства EPLAN Platform предлагают немало инструментов: макросы символов, табличные обработки, объекты-заполнители, экспорт и многое другое. Но со временем, когда база компонентов уже наполнена, схемы и компоновки отлажены, стандартного функционала начинает не хватать — хочется добавить что-то своё. И здесь на помощь приходит EPLAN API.
Главная сложность работы с API — необходимость разбираться не только в базовом функционале EPLAN, но и в языке C#, а также в среде разработки Visual Studio. Таких специалистов немного. Конечно, можно заказать доработки у профессионалов, но бюджет на это есть не всегда, поэтому многим инженерам приходится развивать собственные навыки и осваивать API самостоятельно.
Я сам занимаюсь разработкой РКД на АСУТП и столкнулся с этими трудностями: документация есть, но она скорее справочная, чем обучающая, и примеров для новичков там почти нет. Русскоязычных источников тоже немного: помог Форум АСУТП и одна статья на Хабре про создание простого Add-In.
В этой статье я попробую восполнить пробел: приведу при��еры кода с пояснениями, чтобы начинающим разработчикам было проще войти в тему. Я сознательно оставляю примеры максимально простыми — чтобы сосредоточиться на сути, а не на красоте кода.
Scripts и Add-Ins: в чём разница
Итак, EPLAN Platform позволяет использовать API двумя способами: Scripts и Add-In’s.
Script - это обычный текстовый файл, содержащий код дополнения. Для его запуска можно просто в программе указать путь до файла *.cs и EPLAN его сам компилирует и запустит. Для запуска скриптов не требуется дополнительных лицензий, но они очень ограничены по функционалу: имеют ограниченный доступ к объектной модели EPLAN, отсутствует возможность получения свойств объектов. Обзор структуры и возможностей скриптов приведен тут.
Add-In - это полноценное дополнение, которое компилируется в библиотеку *.dll. Для возможности подключения таких дополнений имеется два вида лицензий: runtime и developer. Runtime лицензия позволяет запускать модули, подписанные самой компанией EPLAN или сертифицированными разработчиками. Developer лицензия позволяет подключать любые библиотеки без подписи.
Для автоматизации взаимодействия с существующими объектами EPLAN возможностей скриптов становится недостаточно, поэтому далее коснемся только разработки Add-In’s.
Создание нового проекта в Visual Studio
В этом разделе частично продублирую статью про создание Add-In'а из-за изменений в интерфейсе MS Visual Studio и API 2025.
При создании нового проекта в MS Visual Studio необходимо выбрать шаблон "Библиотек�� классов (.NET Framework)".

На следующем шаге выберем платформу .NET Framework 4.8.1.

Настройка проекта под EPLAN API
После создания проекта необходимо добавить ссылки на библиотеки Eplan.EplApi, которые расположены в папке установки EPLAN (в моем случае это C:\Program Files\EPLAN2025\Platform\2025.0.3\Bin).

В свойствах проекта в разделе Сборка выбираем целевую платформу x64.

В разделе "Приложение" указываем имя сборки в формате Eplan.EplAddIn.XXXX, где XXXX - имя нашего приложения.

Видео инструкция по созданию проекта Add-In.
Добавляем класс AddInModule.cs
С настройками покончено, теперь необходимо создать 2 класса: IEplAddIn и IEplAction. Первый используется для регистрации Add-In и
создания кнопок на Ribbon-панели, которые будут запускать будущие действия, прописанные в классах IEplAction.
Переименуем автоматически созданный Class1.cs в AddInModule.cs и добавим в него следующее содержание:
using Eplan.EplApi.ApplicationFramework; namespace Test { public class AddInModule : IEplAddIn { public bool OnExit() { return true; } public bool OnInit() { return true; } public bool OnInitGui() { return true; } public bool OnRegister(ref bool bLoadOnStart) { var ribbonBar = new Eplan.EplApi.Gui.RibbonBar(); ribbonBar.AddCommand("Тест", "ActionTest"); bLoadOnStart = true; return true; } public bool OnUnregister() { var ribbonBar = new Eplan.EplApi.Gui.RibbonBar(); ribbonBar.RemoveCommand("ActionTest"); return true; } } }
Начиная с версии EPLAN 2022 классические панели заменены на Ribbon и необходимо создавать и удалять кнопки на этой панели.
В этом классе можно добавлять для кнопок иконки, комментарии, всплывающие подсказки и сортировать их. Справка по использованию Ribbon.
В текущем примере на вкладке “Расширения” в группе “API” создается новая кнопка с текстом “Тест”, выполняющая действие ActionTest, имя которого должно быть задано в отдельном классе: IEplAction в методе OnRegister.
Так как кнопки добавляются и удаляются в методах OnRegister и OnUnregister, то после внесения изменений в этих методах (например, добавления новой кнопки) чтобы увидеть обновленную информацию в Ribbon-панели, необходимо выгрузить Add-In, перезагрузить EPLAN и заново загрузить Add-In.
В целом, уже на этом этапе можно скомпилировать библиотеку и загрузить её в EPLAN. На Ribbon-панели появится дополнительная вкладка “Расширения”, и на ней появится единственная кнопка “Тест”, которая будет неактивной, так как мы ещё не сделали никаких действий для неё.
Добавляем класс ActionTest.cs
Добавим в проект еще один класс (Shift+Alt+C) и назовем его ActionTest.cs
using Eplan.EplApi.ApplicationFramework; using System; using System.Windows.Forms; namespace Test { public class ActionTest : IEplAction { public bool Execute(ActionCallingContext oActionCallingContext) { MessageBox.Show("Hello World"); return true; } public void GetActionProperties(ref ActionProperties actionProperties) { } public bool OnRegister(ref string Name, ref int Ordinal) { Name = "ActionTest"; Ordinal = 20; return true; } } }
Метод OnRegister вызывается при регистрации Add-In, в нем задается имя и приоритет, которые регистрируются в EPLAN. Метод Execute выполняется при вызове Action, например при нажатии на кнопку, которую создали на предыдущем шаге. Более подробная инструкция по Action.
Взаимодействие с объектами EPLAN
Работа с текущим проектом и страницей
Многие методы API EPLAN требуют в качестве аргументов текущий проект и страницу, так что первым делом запишем в переменные текущий проект и первую выделенную страницу:
Project proj = new SelectionSet().GetCurrentProject(false); if (proj == null) return false; Page pg = new SelectionSet().GetSelectedPages().FirstOrDefault();
Выбор и поиск объектов
Для взаимодействия с существующими объектами можно использовать 2 инструмента
SelectionSet - выдает набор всех выделенных элементов
DMObjectsFinder - позволяет найти необходимые объекты по заданным критериям
SelectionSet
В объектной модели EPLAN множество различных типов объектов, и чтобы понять, с чем конкретно вы имеете дело, можно вывести тип выделенного объекта в сообщении:
SelectionSet Set = new SelectionSet(); if (Set.Selection.Count() == 0) { return false; } foreach (StorableObject selObj in Set.Selection) { MessageBox.Show(selObj.ToString()); }
Когда мы уже выяснили, с каким типом объекта имеем дело, то можем преобразовать StorableObject к нужному типу и дальше уже с ним взаимодействовать.
List<Terminal> TerminalsList = new List<Terminal>(); SelectionSet Set = new SelectionSet(); if (Set.Selection.Count() == 0) { return false; } foreach (StorableObject selObj in Set.Selection) { if (selObj is Terminal) { Terminal SelectedTerminal = selObj as Terminal; TerminalsList.Add(SelectedTerminal); } }
Справка по инструменту SelectionSet.
DMObjectsFinder
Возможности поиска DMObjectsFinder хорошо иллюстрируются следующим рисунком.

Краткий пример поиска графических элементов на текущей странице:
DMObjectsFinder oDMO = new DMObjectsFinder(proj); PlacementsFilter filter = new PlacementsFilter { Page = pg }; foreach (Placement pcc in oDMO.GetPlacements(filter)) { if (pcc is GraphicalPlacement) { GraphicalPlacement gp = pcc as GraphicalPlacement; if (gp.Layer.Name == "Выноски") gp.Remove(); } }
В этом примере происходит поиск всех размещенных на текущей странице объектов. Если размещенный объект является графикой (линии, прямоугольники и др.), то проверяем на каком слое он расположен и удаляем в случае, если слой называется "Выноски".
Справка по DMObjectsFinder
Как добавить поддержку Undo и Rollback
По умолчанию после выполнения любого пользовательского Action в EPLAN полностью сбрасывается список отменяемых действий (Отменить ввод CTRL+Z), что очень неудобно, учитывая отсутствие кнопки "Сохранить". Чтобы такого не произошло, необходимо позаботиться о возможности отмены действий в своем Add-In. Для этого существуют 2 инструмента: UndoManager и SafetyPoint, которые можно использовать совместно.
using (UndoStep undoStep = new UndoManager().CreateUndoStep()) { undoStep.SetUndoDescription("DoStuff"); using (SafetyPoint safetyPoint = SafetyPoint.Create()) { try { // Do stuff safetyPoint.Commit(); } catch (Exception exception) { MessageBox.Show(exception.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error); safetyPoint.Rollback(); } } }
При использовании такой конструкции сохраняется возможность отмены действий пользователем, а, при возникновении исключений, выполненные действия отменяются автоматически.
Свойства объектов (Properties и PropertyPlacements)
У каждого объекта в API DataModel имеются свойства (Properties), которые мы можем увидеть на первой вкладке свойств.

Но также у большинства объектов имеются свойства (PropertyPlacements), которые отображаются на листе и которые мы можем настроить на вкладке "Отображение".

Для идентификации PropertyPlacements можно получить название и номер используя следующие поля:
...PropertyPlacements[ ].DisplayedProperty.Definition.Name ...PropertyPlacements[ ].DisplayedProperty.AsInt
Пример скрытия свойства <20008> ОУ (идентифицирующее, без структуры проекта) для объектов обзора модели:
SelectionSet Set = new SelectionSet(); if (Set.Selection.Count() == 0) { return false; } foreach (StorableObject selObj in Set.Selection) { if (selObj is ViewPart) { ViewPart SelectedViewPart = selObj as ViewPart; foreach (PropertyPlacement prop in SelectedViewPart.PropertyPlacements) { if (prop.DisplayedProperty.AsInt == 20008) prop.IsVisible = false; } } }
Помимо PropertyPlacements можно использовать заранее подготовленные порядки свойств (PropertyPlacementsSchemas) и присваивать их выбранному объекту
SelectionSet Set = new SelectionSet(); if (Set.Selection.Count() == 0) { return false; } foreach (StorableObject selObj in Set.Selection) { if (selObj is ViewPart) { ViewPart SelectedViewPart = selObj as ViewPart; SelectedViewPart.Properties.INSTANCE_ACTIVE_PROPERTYSET.Set ("Turbo_Output"); } }
Справка по Properties и PropertyPlacements. Перечень всех свойств EPLAN с возможностью сортировки и фильтрации можно посмотреть здесь.
Доступ к настройкам EPLAN через API
Eplan позволяет через API получить доступ ко всем настройкам как для чтения, так и для записи. Для чтения или записи конкретной настройки необходимо определить её имя. Это можно сделать путем экспорта настроек в xml и дальнейшего нахождения в файле нужной настройки. Например как это выглядит для выбора стиля оформления:


Соответственно имя настройки для использования в Add-In будет USER.MF.GuiColorScheme, а тип возвращаемого значения - int.
Settings oSettings = new Settings(); int colorScheme = oSettings.GetNumericSetting("USER.MF.GuiColorScheme", 0); // 0=Default 1=Dark 2=Light
Справка по свойствам проекта.
Работа с Interaction
В некоторых случаях во время выполнения Action необходимо получить от пользователя информацию, например, координаты курсора в пространстве листа после клика пользователем. В этом случае EPLAN API предоставляет такую возможность в виде класса Interaction. Ниже приведен пример класса, в котором для перемещения свойств (PropertyPlacement) из подготовленного в Action списка объектов пользователю предлагается дважды кликнуть мышкой. Первый клик сохраняет коорди��аты, куда необходимо переместить первое свойство в списке, а второй клик расстояние до последующих свойств.
using Eplan.EplApi.Base; using Eplan.EplApi.DataModel.EObjects; using Eplan.EplApi.EServices.Ged; namespace Test { public class TestInteraction : Interaction { public override RequestCode OnStart(InteractionContext oContext) { Description = "Отменить перемещение"; //Наименование действия для UndoStep, который генерируется автоматически перед выполнением OnSuccess return RequestCode.Point; } public override RequestCode OnPoint(Position oPosition) { //Получение координат курсора после клика FirstPosition = oPosition.FinalPosition; return RequestCode.Length; } public override RequestCode OnLength(double dLength) { //Получение расстояния length = dLength; return RequestCode.Success; } public override void OnSuccess(InteractionContext oContext) { foreach (Terminal term in TestAction.SelectedTerminals) { PointD TempPoint = new PointD (FirstPosition.X - term.Location.X, FirstPosition.Y- term.Location.Y); term.PropertyPlacements[3].Location = TempPoint; FirstPosition.X += length; } } private double length = 0.0; private PointD FirstPosition = new PointD(0, 0); } }
Interaction регистрируется в EPLAN по названию класса. Для вызова Interaction из Action используем ActionManager
string strAction = "XGedStartInteractionAction"; ActionManager oActionManager = new ActionManager(); Eplan.EplApi.ApplicationFramework.Action oAction = oActionManager.FindAction(strAction); if (oAction != null) { ActionCallingContext oContext = new ActionCallingContext(); oContext.AddParameter("Name", "TestInteraction"); bool bRet = oAction.Execute(oContext); /* if (bRet) { new Decider().Decide(EnumDecisionType.eOkDecision, "The Action " + oAction.Name + " ended successfully!", "", EnumDecisionReturn.eOK, EnumDecisionReturn.eOK); } else { new Decider().Decide(EnumDecisionType.eOkDecision, "The Action " + oAction.Name + " ended with errors!", "", EnumDecisionReturn.eOK, EnumDecisionReturn.eOK); } */ return bRet; } else { new Decider().Decide(EnumDecisionType.eOkDecision, "Не удалось найти XGedStartInteractionAction", "Ошибка", EnumDecisionReturn.eOK, EnumDecisionReturn.eOK); return false; }
Важно, чтобы значение параметра Name в строке oContext.AddParameter("Name", "TestInteraction"); точно совпадало с названием класса Interaction. Через oContext.AddParameter можно передавать в Interaction любые строковые данные.
Справка по Interactions.
Итоги: зачем инженеру разбираться в API
API EPLAN открывает действительно широкие возможности. Даже простые Add-In’ы позволяют ускорить рутинные операции и сделать разработку проектов удобнее.
Порог входа, конечно, высокий: нужно разбираться и в объектной модели EPLAN, и в C#, и в особенностях самой платформы. Но чем больше практики — тем быстрее приходит понимание.
Надеюсь, приведённые в статье примеры помогут вам сэкономить время на старте и вдохновят на создание собственных дополнений. Ведь даже одна кнопка на Ribbon-панели, автоматизирующая частое действие, может заметно облегчить жизнь инженера.
