- PVSM.RU - https://www.pvsm.ru -

Зачем SCADA, когда есть .NET?

Я работаю в компании, которая занимается автоматизацией производственных процессов. Знаком не по наслышке с программируемыми логическими контроллерами (PLC), человеко-машинным интерфейсом (HMI) и SCADA (диспетчерское управление и сбор данных).

А еще я увлекаюсь программированием. В основном на языках C# и Java (Android). Когда я познакомился с технологией WPF и увидел как просто на ней реализуется графический интерфейс пользователя, восторгу моему не было предела. «Да это же убийца SCADA-систем», — подумал я. Надо просто каким-то образом связать .NET проект с устройствами ввода-вывода (PLC).

И тут ко мне пришла идея, которая в определенный момент приходит к каждому автоматчику. Сделать свою SCADA-систему. И не просто SCADA-систему, а SCADA-систему для .NET разработчиков (прежде всего для себя самого). Потому что порой когда переключаешься из Visual Studio в редактор «современной» SCADA-системы, хочется вырвать себе последние волосы. Но настоящие трудности начинаются, когда необходимо реализовать функционал, выходящий за рамки базового. Другое дело — мир программистов. Ответ практически на любой вопрос можно быстро найти на сайте stackoverflow.com [1] через его поисковый интерфейс google.com [2].

Далее я опишу то, что у меня получилось сделать. Исходный код проекта доступен по ссылке https://github.com/phmi/phmi [3].

Установка

Для использования PHmi (так я ее назвал) должны быть удовлетворены следующие условия:

  • PHmi
  • СУБД PostgreSQL v9.2 или выше (ее можно скачать с сайта www.postgresql.com [4])
  • Microsoft Visual Studio 2010 или выше (экспресс версия или Community Edition подойдет)

Разработка проекта

Разработку проекта можно разделить на две фазы: конфигурирование сервера и разработку проекта клиента.

Конфигурирование сервера

Запустим PHmiConfigurator.exe. Появится следующее окно:

Зачем SCADA, когда есть .NET? - 1

Нажмем на кнопку “New project”. Появится окно “New project”:

Зачем SCADA, когда есть .NET? - 2

Введем параметры связи с PostgreSQL и желаемое название базы данных:

Зачем SCADA, когда есть .NET? - 3

После нажатия на кнопку “Ok” новый проект будет создан:

Зачем SCADA, когда есть .NET? - 4

Нажмем на кнопку “I/O devices”. В конфигураторе откроется вкладка “I/O devices”:

Зачем SCADA, когда есть .NET? - 5

Добавим новое устройство ввода-вывода “IoDev” типа “Generic”. «Generic» — это устройство ввода вывода для отладочных целей. Либо оно используется, когда не нужно соединяться с реальным устройством. Оно поддерживает любые адреса тегов:

Зачем SCADA, когда есть .NET? - 6

Зачем SCADA, когда есть .NET? - 7

Нажмем на кнопку «Save» и закроем вкладку.
Откроем вкладку “Digital tags” и создадим новый цифровой тег.

Зачем SCADA, когда есть .NET? - 8

Откроем вкладку “Numeric tags” и создадим новый аналоговый тег:

Зачем SCADA, когда есть .NET? - 9

Поля описания, формата, единицы измерения, калибровочные границы необязательны и могут быть опущены.
Создадим новую категорию аварий:

Зачем SCADA, когда есть .NET? - 10

Создадим новый тег аварий:

Зачем SCADA, когда есть .NET? - 11

Создадим категорию трендов:

Зачем SCADA, когда есть .NET? - 12

Создадим новый тег трендов:

Зачем SCADA, когда есть .NET? - 13

Разработка проекта клиента

В конфигураторе нажмем на кнопку “Build client”:

Зачем SCADA, когда есть .NET? - 14

Выберем путь к файлам, чтобы их создать.
«Namespace» должно совпадать с названием будущего проекта Visual Studio.
Нажмем на кнопку “Build”.
По указанному пути должны появиться файлы:

Зачем SCADA, когда есть .NET? - 15

Запустим Microsoft Visual Studio и создадим новый проект WPF. Целевой фреймворк должен быть “.Net Framework 4.0” или выше:

Зачем SCADA, когда есть .NET? - 16

Зачем SCADA, когда есть .NET? - 17

Добавим ссылку к PHmiClient.dll:

Зачем SCADA, когда есть .NET? - 18

Зачем SCADA, когда есть .NET? - 19

Зачем SCADA, когда есть .NET? - 20

Добавим файлы PHmi.cs и PHmiResources.resx, созданные заранее:

Зачем SCADA, когда есть .NET? - 21

Зачем SCADA, когда есть .NET? - 22

Кликнем двойным щелчком по PHmiResources.resx и поменяем “Access modifier” на Public:

Зачем SCADA, когда есть .NET? - 23

Откроем MainWindow.xaml. Добавим кнотрол Root в корневой Grid.

<Window x:Class="ClientSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:pHmiControls="clr-namespace:PHmiClient.Controls;assembly=PHmiClient"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <pHmiControls:Root/>
    </Grid>
</Window>

Привяжем PHmi к DataContext Rootа. Для этого создадим новый объект PHmi в ресурсах окна.

<Window x:Class="ClientSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:pHmiControls="clr-namespace:PHmiClient.Controls;assembly=PHmiClient"
        xmlns:clientSample="clr-namespace:ClientSample"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <clientSample:PHmi x:Key="PHmi"/>
    </Window.Resources>
    <Grid>
        <pHmiControls:Root DataContext="{StaticResource PHmi}"/>
    </Grid>
</Window>

Добавим папку «Pages» для страниц. Добавим туда UserControl под названием «HomePage».

Зачем SCADA, когда есть .NET? - 24

Страницы должны реализовывать интерфейс IPage. Листинг HomePage.xaml.cs представлен ниже.

using PHmiClient.Controls.Pages;
using System;
using System.Windows.Controls;

namespace ClientSample.Pages
{
    /// <summary>
    /// Interaction logic for HomePage.xaml
    /// </summary>
    public partial class HomePage : UserControl, IPage
    {
        public HomePage()
        {
            InitializeComponent();
        }

        public object PageName
        {
            get { return "Home page"; }
        }

        public PHmiClient.Controls.IRoot Root
        {
            get; set;
        }
    }
}

А вот разметка Xaml файла:

<UserControl x:Class="ClientSample.Pages.HomePage"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:clientSample="clr-namespace:ClientSample"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
             d:DataContext="{d:DesignInstance clientSample:PHmi, IsDesignTimeCreatable=True}">
    <Grid>
        <StackPanel>
            <TextBlock Text="{Binding Path=IoDev.DigitalTag.Value}"/>
            <TextBlock Text="{Binding Path=IoDev.NumericTag.ValueString}"/>
        </StackPanel>
    </Grid>
</UserControl>

Мы добавили TextBlock для отображения значения тегов.
Привяжем тип домашней страницы к Root:

<Window x:Class="ClientSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:pHmiControls="clr-namespace:PHmiClient.Controls;assembly=PHmiClient"
        xmlns:clientSample="clr-namespace:ClientSample"
        xmlns:pages="clr-namespace:ClientSample.Pages"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <clientSample:PHmi x:Key="PHmi"/>
    </Window.Resources>
    <Grid>
        <pHmiControls:Root
            DataContext="{StaticResource PHmi}"
            HomePage="{x:Type pages:HomePage}"/>
    </Grid>
</Window>

Запустим приложение и посмотрим, что получилось:

Зачем SCADA, когда есть .NET? - 25

Значений тегов не видно. Это потому что не запущен PHmiRunner.exe. Нажмем кнопку “Run” в конфигураторе.

Зачем SCADA, когда есть .NET? - 26

Теперь добавим на страницу элементы управления.

<UserControl x:Class="ClientSample.Pages.HomePage"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:clientSample="clr-namespace:ClientSample"
             xmlns:pHmiControls="clr-namespace:PHmiClient.Controls;assembly=PHmiClient"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
             d:DataContext="{d:DesignInstance clientSample:PHmi, IsDesignTimeCreatable=True}">
    <Grid>
        <StackPanel>
            <TextBlock Text="{Binding Path=IoDev.DigitalTag.Value}"/>
            <TextBlock Text="{Binding Path=IoDev.NumericTag.ValueString}"/>
            <CheckBox
                IsChecked="{Binding Path=IoDev.DigitalTag.Value, Mode=TwoWay}"
                Content="{Binding Path=IoDev.DigitalTag.Description}"/>
            <pHmiControls:NumericInput NumericTag="{Binding Path=IoDev.NumericTag}"/>
        </StackPanel>
    </Grid>
</UserControl>

Зачем SCADA, когда есть .NET? - 27

Если включить “Digital tag", возникнет авария.
Добавим на страницу тренд.

<UserControl x:Class="ClientSample.Pages.HomePage"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:clientSample="clr-namespace:ClientSample"
             xmlns:pHmiControls="clr-namespace:PHmiClient.Controls;assembly=PHmiClient"
             xmlns:trends="clr-namespace:PHmiClient.Controls.Trends;assembly=PHmiClient"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
             d:DataContext="{d:DesignInstance clientSample:PHmi, IsDesignTimeCreatable=True}">
    <Grid>
        <StackPanel>
            <TextBlock Text="{Binding Path=IoDev.DigitalTag.Value}"/>
            <TextBlock Text="{Binding Path=IoDev.NumericTag.ValueString}"/>
            <CheckBox
                IsChecked="{Binding Path=IoDev.DigitalTag.Value, Mode=TwoWay}"
                Content="{Binding Path=IoDev.DigitalTag.Description}"/>
            <pHmiControls:NumericInput NumericTag="{Binding Path=IoDev.NumericTag}"/>
            <trends:TrendViewer Height="400">
                <trends:TrendPen
                    TrendTag="{Binding Path=Trends.NumericTagTrend}"
                    Brush="Red"/>
            </trends:TrendViewer>
        </StackPanel>
    </Grid>
</UserControl>

Зачем SCADA, когда есть .NET? - 28

Задание языка окна

Для отображения строк в соответствии с региональными настройками (дата, время и прочее), необходимо изменить XmlLanguage в конструкторе окна:

using System.Threading;
using System.Windows;
using System.Windows.Markup;

namespace ClientSample
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            Language = XmlLanguage.GetLanguage(
                Thread.CurrentThread.CurrentUICulture.IetfLanguageTag);
            InitializeComponent();
        }
    }
}

Спасибо!

Автор: phmi

Источник [5]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/programmirovanie/80200

Ссылки в тексте:

[1] stackoverflow.com: http://stackoverflow.com/

[2] google.com: http://google.com

[3] https://github.com/phmi/phmi: https://github.com/phmi/phmi

[4] www.postgresql.com: http://www.postgresql.com

[5] Источник: http://habrahabr.ru/post/248423/