Как стать автором
Обновить

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

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

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

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

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

Установка


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


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


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

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

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



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



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



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



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



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





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



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



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



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



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



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



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

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



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



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





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







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





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



Откроем 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».



Страницы должны реализовывать интерфейс 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>

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



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



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

<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>



Если включить “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>




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

Для отображения строк в соответствии с региональными настройками (дата, время и прочее), необходимо изменить 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();
        }
    }
}

Спасибо!
Теги:
Хабы:
Всего голосов 15: ↑10 и ↓5+5
Комментарии25

Публикации

Истории

Работа

.NET разработчик
76 вакансий

Ближайшие события

Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн
Антиконференция X5 Future Night
Дата30 мая
Время11:00 – 23:00
Место
Онлайн
Конференция «IT IS CONF 2024»
Дата20 июня
Время09:00 – 19:00
Место
Екатеринбург