Каталог
Зателефонуйте мені
Каталог

Як створити кастомний контрол для WinCC Advanced на C# з використанням Siemens.Runtime.ControlDev.dll

Як створити кастомний контрол для WinCC Advanced на C# з використанням Siemens.Runtime.ControlDev.dll
Автор: Andriy Savechka Опубліковано: 15.11.2025 Переглядів: 84 Коментарів: 0

У цій статті я покажу, як створити власний кастомний контрол для WinCC Advanced на C#, використовуючи бібліотеку Siemens.Runtime.ControlDev.dll. Ми розробимо таблицю, яка відображатиме дані з бази MSSQL, розберемо налаштування Visual Studio та інтеграцію з TIA Portal

Вступ

WinCC Advanced підтримує використання кастомних контролів — це спеціальні елементи інтерфейсу, які розробник може створити самостійно на C# і підключити до проєкту HMI так само, як будь-який стандартний компонент. Вони дозволяють розширити можливості WinCC у тих випадках, коли штатних елементів дуже мало або їх функціонал обмежений.

Власний контрол потрібен тоді, коли необхідно реалізувати нестандартну логіку, підключити зовнішні сервіси, працювати з базами даних, побудувати складні таблиці, графіку або створити елемент візуалізації, якого немає у стандартній бібліотеці WinCC.

Для взаємодії кастомного контролу з середовищем WinCC Advanced використовується бібліотека Siemens.Runtime.ControlDev.dll. Саме вона забезпечує життєвий цикл елемента, обробку подій, збереження властивостей та коректну роботу контролу в Runtime.

У цій статті ми створимо практичний приклад — табличний контрол, який буде отримувати дані з бази даних MSSQL та відображати їх безпосередньо на екрані HMI. Це дозволить побачити повний процес — від написання коду в Visual Studio до інтеграції готового кастомного елемента у WinCC Advanced.

Необхідні інструменти та середовище

Для розробки кастомного контролу під WinCC Advanced потрібен мінімальний набір інструментів, але кожен із них виконує свою важливу роль. Нижче наведено основне програмне забезпечення, яке потрібно встановити перед початком роботи.

Visual Studio (рекомендовано 2019 або 2022)

Visual Studio буде основним середовищем, у якому створюється проєкт C#.
Необхідні вимоги:

  • встановлена підтримка .NET Framework 4.7.2 або 4.8
  • шаблони Class Library або Windows Forms Control Library
  • бажано мати встановлену підтримку Windows Forms Designer

Хоча Siemens офіційно орієнтується на старіші версії середовищ, Visual Studio 2019/2022 працює стабільно та зручніше для розробки.

WinCC Advanced / TIA Portal

Для інтеграції кастомного контролу потрібна будь-яка сучасна версія:

  • TIA Portal V15.1 – V19
  • компонент WinCC Advanced
  • інстальована Runtime (щоб тестувати контрол у реальних умовах)

Саме TIA Portal імпортує вашу DLL і дозволяє додати контрол на екран HMI як звичайний елемент.

MSSQL Server (2016–2022)

Це джерело даних, з яким буде працювати наш кастомний контрол.
Підійде будь-яка версія:

  • MSSQL Express
  • MSSQL Standard
  • MSSQL Developer

Потрібно також мати:

  • SQL Management Studio (SSMS) для роботи із запитами
  • таблицю або VIEW, з якої контрол буде зчитувати дані
  • доступ до сервера з HMI Runtime (локально або по мережі)

Siemens.Runtime.ControlDev.dll

Бібліотека, що забезпечує зв’язок між користувацьким контролом і WinCC Advanced.
Файл зазвичай знаходиться тут:

C:\Program Files\Siemens\Automation\WinCC\RTAdvanced\Bin\


Його потрібно додати у Visual Studio як Reference, але обов’язково з Copy Local = False (інакше контрол не завантажиться в Runtime).

Підготовка проєкту в Visual Studio

Відкриваємо VS та створюємо новий проект

В полі пошуку пишемо слово class та вибираємо як тип проекту Class Library(.NET Framework). Даємо проекту назву(в моєму випадку це DBRecipe) та тиснемо кнопку Create

 

далі йдемо в папку де у нас встановлено WinCC RT та шукаємо файл з назвою Siemens.Runtime.ControlDev.dll

копіюємо його до папки з нашим проектом

після чого підключажмо його як Reference до нашого проекту

в результаті у вас має вийти як на картинці нижче

Код контрола

Відкриваємо дизайнер форми та додаємо на неї DataGridView

Встановимо для контрола наступні властивості. Нижче я наведу їх у вигляді коду, хоча ви можете зробити те саме через редактор властивостей (Properties)


// 
// dataGridView
// 
this.dataGridView.AllowUserToAddRows = false;
this.dataGridView.AllowUserToDeleteRows = false;
dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.ActiveBorder;
dataGridViewCellStyle1.ForeColor = System.Drawing.Color.Black;
dataGridViewCellStyle1.SelectionBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(192)))), ((int)(((byte)(0)))));
this.dataGridView.AlternatingRowsDefaultCellStyle = dataGridViewCellStyle1;
this.dataGridView.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill;
this.dataGridView.BackgroundColor = System.Drawing.Color.Silver;
this.dataGridView.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.dataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
dataGridViewCellStyle2.BackColor = System.Drawing.SystemColors.InactiveCaption;
dataGridViewCellStyle2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
dataGridViewCellStyle2.ForeColor = System.Drawing.SystemColors.ControlText;
dataGridViewCellStyle2.SelectionBackColor = System.Drawing.SystemColors.Highlight;
dataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
this.dataGridView.DefaultCellStyle = dataGridViewCellStyle2;
this.dataGridView.Dock = System.Windows.Forms.DockStyle.Fill;
this.dataGridView.Location = new System.Drawing.Point(0, 0);
this.dataGridView.MultiSelect = false;
this.dataGridView.Name = "dataGridView";
this.dataGridView.ReadOnly = true;
this.dataGridView.RowHeadersVisible = false;
this.dataGridView.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
this.dataGridView.Size = new System.Drawing.Size(631, 263);
this.dataGridView.TabIndex = 1;
this.dataGridView.CellClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridView_CellClick);
this.dataGridView.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridView_CellContentClick);

Далі перейдемо безпосередньо до коду контрола. Я намагався максимально використовувати коментарі, щоб вам було зрозуміло, що саме тут відбувається. Нижче наведено повний лістинг: можете уважно його розглянути, після чого я коротко зупинюся на основних моментах. 


using System;
using System.ComponentModel;
using System.Data;
using Siemens.Runtime.ITag;
using System.Windows.Forms;
using System.Data.SqlClient;

namespace DBRecipe
{
    public partial class RecipeView : UserControl, ITagSink
    {
        // Унікальний ідентифікатор реєстрації в системі тегів WinCC
        private int m_RegisterCookie;

        // Інтерфейс взаємодії з WinCC тегами
        private ITag MyTag;

        public RecipeView()
        {
            InitializeComponent();
        }

        // Демо-властивість для ITag (не використовується в даному прикладі)
        private ITag _valInt;
        [Category("General"), Description("User name")]
        public ITag valInt
        {
            get { return _valInt; }
            set { _valInt = value; }
        }

        //------------------------------------------------------------------------
        // Налаштування SQL-з'єднання (editable properties)
        //------------------------------------------------------------------------

        SqlConnection _myConnection = new SqlConnection();

        private string _server = "DESKTOP-Q5SAKSI\\WINCCPLUSMIG2014";
        private string _user = "dbuser";
        private string _pass = "1111";
        private string _dbname = "Stats";
        private string _sql = "select * from recipes";
        private string _outTagname = "RECIPE_ROW";

        // Адреса SQL сервера
        [Category("General"), Description("Server address")]
        public string Server
        {
            get { return _server; }
            set { _server = value; }
        }

        // Логін до SQL
        [Category("General"), Description("User name")]
        public string User
        {
            get { return _user; }
            set { _user = value; }
        }

        // Пароль (скривається у PropertyGrid)
        [PasswordPropertyText(true), Category("General"), Description("Password")]
        public string Pass
        {
            get { return _pass; }
            set { _pass = value; }
        }

        // Назва БД
        [Category("General"), Description("Name of DB")]
        public string DBName
        {
            get { return _dbname; }
            set { _dbname = value; }
        }

        // SQL-запит. Після зміни — автоматично викликається EXEL_SQL()
        [Category("General"), Description("Query string")]
        public string SQL
        {
            get { return _sql; }
            set
            {
                if (value != null)
                {
                    _sql = value;
                    EXEL_SQL();   // автооновлення таблиці при зміні запиту
                }
            }
        }

        // Тег WinCC, в який буде записуватись вибраний рядок
        [Category("General"), Description("Name of wincc tag where data will out")]
        public string OutTagname
        {
            get { return _outTagname; }
            set { _outTagname = value; }
        }

        //------------------------------------------------------------------------
        // Виконання SQL запиту та заповнення DataGridView
        //------------------------------------------------------------------------
        private void EXEL_SQL()
        {
            // Формуємо connection string
            _myConnection.ConnectionString =
                "user id=" + _user +
                ";password=" + _pass +
                ";server=" + _server +
                ";Trusted_Connection=no;" +
                "database=" + _dbname +
                ";connection timeout=10";

            // Створюємо адаптер для SELECT запиту
            using (SqlDataAdapter a = new SqlDataAdapter(_sql, _myConnection))
            {
                // Результат у DataTable
                DataTable t = new DataTable();
                a.Fill(t);

                // Відображення у таблиці на екрані
                dataGridView.DataSource = t;
            }
        }

        //------------------------------------------------------------------------
        // Клік по комірці таблиці → запис у WinCC тег
        //------------------------------------------------------------------------
        private void dataGridView_CellClick(object sender, DataGridViewCellEventArgs e)
        {
            string _data = null;

            // Ігноруємо заголовок таблиці
            if (e.RowIndex >= 0)
            {
                DataGridViewRow selectedRow = dataGridView.Rows[e.RowIndex];

                // Формуємо рядок значень через ";"
                foreach (DataGridViewCell cell in selectedRow.Cells)
                {
                    _data += cell.Value.ToString() + ";";
                }

                // Значення, яке підемо записувати у тег
                object TV = _data.TrimEnd(';');
                object TN = _outTagname;

                // Запис у тег WinCC (асинхронний)
                MyTag.WriteTagAsync(m_RegisterCookie, TN, 45, TV);
            }
        }

        //------------------------------------------------------------------------
        // Ініціалізація контролу при завантаженні
        //------------------------------------------------------------------------
        private void RecipeView_Load(object sender, EventArgs e)
        {
            this.ChangeTagServer(); // реєструємось у Tag server WinCC
        }

        //------------------------------------------------------------------------
        // Безпечне від’єднання від Tag Server при закритті
        //------------------------------------------------------------------------
        private void RecipeView_Disposed(object sender, EventArgs e)
        {
            if (this.MyTag == null)
                return;

            this.MyTag.Unregister(this.m_RegisterCookie); // зняття реєстрації
            this.MyTag = null;
        }

        //------------------------------------------------------------------------
        // Реєстрація контролу в системі тегів WinCC
        //------------------------------------------------------------------------
        private void ChangeTagServer()
        {
            // В режимі дизайну не виконуємо взаємодію з Runtime
            if (this.DesignMode) return;

            // Отримуємо доступ до ITag через Site Service
            this.MyTag = (ITag)this.Site.GetService(typeof(ITag));

            // Реєстрація контролу, отримання cookie
            this.m_RegisterCookie = this.MyTag.Register((ITagSink)this);
        }

        //------------------------------------------------------------------------
        // Обробники подій тегів WinCC (поки не реалізовано)
        //------------------------------------------------------------------------
        public void OnDataChanged(int RegisterCookie, object TagNames, object Values, object Qualities, object VarStates, object Timestamps, object ClientCookies)
        {
            throw new NotImplementedException();
        }

        public void OnWriteComplete(int RegisterCookie, object TagNames, object ClientCookies)
        {
            throw new NotImplementedException();
        }

        public void OnRemoved(int RegisterCookie, object ClientCookies)
        {
            throw new NotImplementedException();
        }

        public void OnCanceled(int RegisterCookie)
        {
            throw new NotImplementedException();
        }

        public void OnError(int RegisterCookie, object TagNames, object ClientCookies, object Errors)
        {
            throw new NotImplementedException();
        }

        private void dataGridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {
            // Не використовується
        }
    }
}

Основні аспекти роботи коду контрола

Розглянемо ключові блоки, які забезпечують працездатність нашого кастомного контрола:

1. Реалізація інтерфейсу ITagSink Наш клас RecipeView успадковує інтерфейс ITagSink. Це обов'язкова умова для контролів WinCC Advanced, оскільки саме цей інтерфейс дозволяє контролу «спілкуватися» з Runtime-системою тегів Siemens. Хоча методи OnDataChanged та OnWriteComplete у цьому прикладі не розписані, їх наявність необхідна для успішної реєстрації контрола в системі.

2. Реєстрація в Tag Server Метод ChangeTagServer() є критично важливим. Він виконується при завантаженні контрола (RecipeView_Load).

  • Ми використовуємо this.Site.GetService(typeof(ITag)) для отримання доступу до сервісу тегів.

Важливо: ми обов'язково перевіряємо this.DesignMode, щоб код не намагався підключитися до WinCC Runtime, поки ви просто працюєте в редакторі Visual Studio.

3. Зв'язок з SQL-базою та динамічні властивості У коді визначено ряд публічних властивостей (Server, User, DBName, SQL тощо).

  • Використання атрибутів типу [Category("General"), Description(...)] дозволяє цим властивостям красиво відображатися в інспекторі об'єктів WinCC.
  • Зверніть увагу на властивість SQL: як тільки ви змінюєте текст запиту в WinCC, автоматично викликається метод EXEL_SQL(), який оновлює дані в таблиці DataGridView.

4. Робота з DataGridView та формування даних Метод EXEL_SQL() виконує стандартний запит до бази даних через SqlDataAdapter. Отримані дані заповнюють DataTable, яка стає джерелом даних (DataSource) для нашої таблиці. Це дозволяє миттєво відобразити вміст бази даних (наприклад, список рецептів) безпосередньо на екрані панелі оператора.

5. Запис обраного рядка в тег WinCC Найцікавіша частина відбувається в обробнику події dataGridView_CellClick. Коли оператор натискає на будь-який рядок:

  • Програма збирає всі значення комірок цього рядка в один рядок, розділяючи їх крапкою з комою (;).
  • За допомогою методу MyTag.WriteTagAsync цей сформований рядок асинхронно записується в тег WinCC, ім'я якого вказано у властивості OutTagname.
  • Це дозволяє WinCC отримати повний набір даних обраного рецепта через один внутрішній або зовнішній тег.

6. Коректне завершення роботи У методі RecipeView_Disposed ми викликаємо Unregister. Це «правило хорошого тону», яке гарантує, що після закриття екрана контрол не залишить після себе «сміття» в пам'яті системи тегів WinCC.

Інтеграція контрола в TIA Portal

Після успішної компіляції контрола копіюємо отриману бібліотеку в папку, де встановлено WinCC Runtime. Це необхідно для того, щоб Runtime гарантовано зміг завантажити її під час запуску. Теоретично контрол можна зареєструвати в будь-якому місці системи, але в такому разі доведеться налаштовувати права доступу в ОС, що може бути проблематично, особливо якщо комп'ютер знаходиться в домені.

Створюємо новий проєкт та додаємо до нього пристрій WinCC RT Advanced

Додаємо новий екран. У панелі інструментів (Toolbox) праворуч відкриваємо розділ "My controls", натискаємо праву кнопку миші та вибираємо пункт "Select objects..."

Переходимо на вкладку "Custom .NET controls" та додаємо наш контрол до списку

Перетягуємо наш контрол на екран (Drag-and-Drop)» , просто перетягнувши його з панелі інструментів

Переходимо на вкладку властивостей контрола (Properties), відкриваємо розділ "General" і бачимо там властивості, які ми заклали в коді, а також їхні значення за замовчуванням.

Вводимо дані для доступу до бази даних SQL-сервера. Про те, як створити базу та таблицю з даними, ви можете прочитати у статті: "Як зібрати статистику з WinCC RT у MS SQL та експортувати в Excel"

Зверніть увагу: якщо ви використовуєте цей приклад для тестування, майте на увазі, що в MS SQL Server за замовчуванням часто активована лише Windows-авторизація. Тому вам необхідно зайти в налаштування сервера, змінити режим перевірки логіну на "SQL Server and Windows Authentication mode", а також створити відповідного користувача з правами доступу до бази

Proof of Concept: Перевірка роботи в Runtime

Аби не бути голослівним, швидко зберемо робочий проєкт із використанням нашого контрола.

Для цього спочатку створимо всі необхідні теги, які знадобляться нам для тестування.

Далі нам знадобиться невеликий скрипт, який буде розкладати отриманий рядок по окремих тегах


Sub ParseRow()
  Dim arrMyArray
  arrMyArray = Split(SmartTags("RowData"),";")
  
  SmartTags("recID") = arrMyArray(0)
  SmartTags("valName") = arrMyArray(1)
  SmartTags("valA1") = arrMyArray(2)
  SmartTags("valA2") = arrMyArray(3)
  SmartTags("valA3") = arrMyArray(4)
  SmartTags("valA4") = arrMyArray(5)
  SmartTags("valA5") = arrMyArray(6)
  SmartTags("valA6") = arrMyArray(7)

End Sub

На головному вікні створюємо кнопку для відкриття спливаючого вікна (Pop-up screen), а також поля вводу (IO Fields), до яких прив’язуємо створені раніше теги

Створюємо саме спливаюче вікно (Pop-up screen): розміщуємо на ньому наш контрол із правильно налаштованими властивостями,

а також кнопку, яка закриватиме вікно й одночасно запускатиме скрипт розбору рядка на теги. Для більшої наочності додаємо поле виводу (IO Field), до якого прив’язуємо тег із сирими даними виділеного рядка.

Схрещуємо пальці на лівій нозі та запускаємо Runtime!

Якщо ви все зробили правильно, то результат має бути таким, як на зображенні вище.

Висновок

Використання кастомних .NET Controls у середовищі WinCC RT Advanced — це потужний інструмент, який дозволяє вийти за межі стандартних можливостей TIA Portal. Ми на практиці переконалися, що інтеграція власного елемента керування не лише спрощує роботу з великими масивами даних (як у нашому прикладі з SQL-таблицею), але й робить інтерфейс оператора більш сучасним та функціональним.

Головні переваги такого підходу:

  • Гнучкість: Ви самі вирішуєте, як відображати та обробляти дані.
  • Масштабованість: Один раз написаний контрол можна використовувати у багатьох проєктах.
  • Продуктивність: Пряма робота з БД через C# часто ефективніша за громіздкі скрипти всередині HMI.

Звісно, цей шлях вимагає базових знань програмування та уважності до налаштувань безпеки (як-от режим авторизації SQL-сервера), але результат у вигляді зручного та швидкого інтерфейсу того вартий.

Як бачите, написати свій власний контрол не так страшно і складно, як могло здатися на перший погляд. Головне — почати, а далі можливості для кастомізації ваших проєктів стануть майже безмежними.

Для тих, хто хоче детальніше розібратися в коді або використати готові напрацювання, я залишаю посилання на репозиторій: [Посилання на GitHub / вихідний код]

Успіхів у розробці!

Коментарі

Додайте коментар...

Ім'я
E-mail (Не буде опублікований)
Ваш коментар
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.
Авторизація
Немаєте акаунта? Реєстрація
Забыли пароль?
E-mail
Введите e-mail Вашей учетной записи, чтобы получить пароль.
Введите корректно e-mail!
viber-chatЧат «А2М» в Viber telegram-chatЧат «А2М» в Telegram
Telegram QR
💬 Актуальні ціни
завжди під рукою