Примеры применения EPLAN API для создания собственного модуля Add In

в 19:56, , рубрики: add-in, api, eplan, АСУТП

Введение

Привет!

Проектирование конструкторской документации для систем автоматизации в 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)".

Примеры применения EPLAN API для создания собственного модуля Add In - 1

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

Примеры применения EPLAN API для создания собственного модуля Add In - 2

Настройка проекта под EPLAN API

После создания проекта необходимо добавить ссылки на библиотеки Eplan.EplApi, которые расположены в папке установки EPLAN (в моем случае это C:Program FilesEPLAN2025Platform2025.0.3Bin).

Примеры применения EPLAN API для создания собственного модуля Add In - 3

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

Примеры применения EPLAN API для создания собственного модуля Add In - 4

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

Примеры применения EPLAN API для создания собственного модуля Add In - 5

Видео инструкция по созданию проекта 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 хорошо иллюстрируются следующим рисунком.

Примеры применения EPLAN API для создания собственного модуля Add In - 6

Краткий пример поиска графических элементов на текущей странице:

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), которые мы можем увидеть на первой вкладке свойств.

Примеры применения EPLAN API для создания собственного модуля Add In - 7

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

Примеры применения EPLAN API для создания собственного модуля Add In - 8

Для идентификации 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 и дальнейшего нахождения в файле нужной настройки. Например как это выглядит для выбора стиля оформления:

Примеры применения EPLAN API для создания собственного модуля Add In - 9
Примеры применения EPLAN API для создания собственного модуля Add In - 10

Соответственно имя настройки для использования в 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-панели, автоматизирующая частое действие, может заметно облегчить жизнь инженера.

Полезные ссылки

EPLAN API Help System

Форум АСУТП

Suplanus EPLAN API Blog

Suplanus EPLAN API GitHub

Автор: tolyantez

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js