Pull to refresh

Симулятор для тестирования ПО АСУТП: Пример

Reading time 8 min
Views 23K
За время, прошедшее с публикации первой статьи на хабрахабре, появилось желание поделиться с сообществом некоторыми мыслями, а так же более подробно описать процесс создания программного симулятора автоматизируемого оборудования. Желающих ознакомиться с предыдущим текстом, прошу сюда — Симулятор для тестирования ПО АСУТП.

Теперь на сайте проекта выложена полная версия программы. После некоторых размышлений, пришёл к выводу, что развить данную поделку до коммерческого продукта не получится. Буду рад отзывам от коллег по цеху, если кому-то пригодиться.

Задача, которая была выбрана в качестве примера, относится скорее к обработке сигналов, чем к АСУТП, и не имеет абсолютно ни какого практического смысла. Сделано это намеренно, для того чтобы не отвлекать читателя излишними подробностями предметной области и увеличить наглядность (все любят графики).



Введение


Почти в каждой статье про TDD (Test-driven development) или Unit testing которую читал, обсуждалась целесообразность расходования времени отведённого на проект для создания тестов. В случае с симулятором для тестирования ПО АСУТП всё несколько сложнее, так как создать полностью достоверную модель динамического производственного процесса по силам, наверное, какому-нибудь институту или очень серьёзным специалистам.

Для себя, выбрал некоторую грань достоверности модели, после которой временные затраты на её создание себя не оправдывают. Честно признаюсь, очень не хватает знаний технологии, физики и математики, поэтому обычно ограничиваюсь симуляцией работы оборудования и делаю простое изменение физического параметра по времени для того, что бы проверить автоматизированные функции и регулирование.

Человеку свойственно ошибаться, поэтому даже при использовании самой простой модели, всплывает множество ошибок. Кроме того, стоит, некоторое время “поиграться” с системой и начинаешь задавать вопросы, о которых раньше бы задумался только на этапе ПНР.

В любом случае, за всё время работы, я ещё не разу не пожалел о времени потраченном на создание симулятора, так как это намного удобнее, чем огромные таблицы мониторинга и модификации переменных. Даже приезжая на объект, я часто использую его для отладки. Можно в любой момент переключить всю систему на виртуальную модель и спокойно, без нервов, сделать свою работу.

Задача


Пример, который я продемонстрирую, навеян жалкими остатками знаний по электротехнике, которые сохранились с университета. Наверное, многие помнят схему преобразователя переменного напряжения с полным диодным мостом и фильтром на конденсаторе (картинка взята по ссылке www.cqham.ru/pow2_15.htm):

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

Решение


После запуска “Process Simulator” можно сразу приступить к созданию переменных (Item->Items Dictionary).
Items Dictionary


Создадим три внутренние переменные:
  • “Sin” — исходный синусоидальный сигнал
  • “Positive” – значение после преобразования в пульсирующий сигнал
  • “Filter” — результат после сглаживающего фильтра

Внутренние переменные предназначены для связи объектов симуляции между собой. Чуть попозже, я объясню, как происходит связь симулятора с внешним миром (ПЛК или другим программным обеспечением).

Создание объектов симуляции осуществляется с помощью менеджера (Simulation->Simulation Objects).
Simulation Objects


Первое что нам понадобится – исходный синусоидальный сигнал. Создаём объект — “Analog generator” и настраиваем его на переменную “Sin”.
Analog generator


Для реализации интерфейса пользователя, необходимо создать экран (Visualization->Add Screen) на котором будем отображать созданные нами объекты (Visualization->Add ViewControl).
ViewControls


Каждый тип объекта имеет некоторое количество способов отображения. При нажатии кнопки “Add”, выбранный тип отображения помещается на текущий экран, который мы уже создали. Для того чтобы изменить позицию на экране, необходимо перейти в режим дизайна (Visualization->Design Mode) и перетащить отображение мышкой. Если отображение поддерживает изменение размера, это также можно сделать, в режиме дизайна.

Теперь необходимо преобразовать синусоидальный сигнал в пульсирующий. Для этого можно создать объект позволяющий писать скрипты на С# – “CSharpScript”.
CSharpScript с текстом скрипта


Скрипт более чем примитивен и в комментариях не нуждается, кроме того, что к нашим переменным “Sin” и “Positive” идёт обращение через их псевдонимы “var0” и “var1”, которые являются C# объектами и должны быть заранее задекларированы (Items ±).

Остановлюсь немного на типе этого объекта. С помощью стандартных функций платформы .Net, написанный нами скрипт компилируется в MSIL, поэтому скорость исполнения достаточно хорошая. Частоту запуска скрипта можно регулировать (Trigger Time). Ошибки на этапе компиляции не позволят создать объект. Если ошибки возникнут на этапе исполнения или время исполнения превысит допустимое (Watchdog), объект будет деактивирован и текст ошибки запишется в лог. Кстати, активировать или деактивировать можно объект любого типа в любое время, через менеджер (Simulation->Simulation Objects->Activate/Deactivate) или через контекстное меню отображения объекта на экране.

Завершающая стадия – фильтрация. Для этого создадим объект “First-order lag” с входной переменной “Positive” и выходной “Filter”.
First-order lag



Результат


После добавления некоторых вспомогательных объектов и размещения отображений на экране получаем следующий результат:

Теперь можно, например, поменять амплитуду сигнала, период или другие параметры и сразу увидеть изменения.

Проект сохранённый в формате XML
<ProcessSimulator WindowState="Normal" Top="152" Left="272" Height="703" Width="1463" Split="354">
  <Items OPC_Host="" OPC_Server="" UseAccessPath="False" Separator="." IgnoreRoot="False" UpdateRate="0" UseASyncWrite="False" ReducedOPCItemIDs="False" S7PLCSim="False" S7ProSimInstance="1">
    <Item Type="Internal" DataType="System.Double" Value="-76.1457525570486" ID="Sin" Comment="Синусоидальный сигнал" />
    <Item Type="Internal" DataType="System.Double" Value="77.117550442077" ID="Positive" Comment="Положительный сигнал" />
    <Item Type="Internal" DataType="System.Double" Value="152.754176188765" ID="Filter" Comment="Сигнал после фильтра" />
  </Items>
  <SimulationObjects RefreshRate="0">
    <SimulationObject Name="Filter" Active="True" Type="First-order lag" InValueItemID="Positive" OutValueItemID="Filter" LagMS="20000" Gain="1" />
    <SimulationObject Name="Positive" Active="True" Type="CSharpScript" Watchdog="1000" TriggerTime="1">
      <ItemIDs>
        <Var Value="Sin" />
        <Var Value="Positive" />
      </ItemIDs>
      <Script><![CDATA[if((double)var0 >= 0.0D)
{
    var1 = (double)var0;
}
else
{
    var1 = -(double)var0;    
}]]></Script>
    </SimulationObject>
    <SimulationObject Name="Positive Output" Active="True" Type="Analog sensor" RawValueItemID="" PhysicalValueItemID="Positive" MaxPhysicalValue="220" MinPhysicalValue="-220" MaxRawValue="27648" MinRawValue="0" Units="" />
    <SimulationObject Name="Sine" Active="True" Type="Analog generator" ValueItemID="Sin" Signal="Sine" Bias="-220" Amplitude="220" Period="20000" Turn="0" />
  </SimulationObjects>
  <Screens>
    <Screen Name="Rectifier" Open="true">
      <ViewControl SimulationObject="Positive" Type="Items" X="615" Y="370" Width="230" Height="96" FirstColumnWidth="78" />
      <ViewControl SimulationObject="Positive Output" Type="Trend" X="481" Y="0" Width="481" Height="324" UpdateRate="100" TimeFrame="1" Color="FF0000C0" />
      <ViewControl SimulationObject="Filter" Type="Trend" X="966" Y="0" Width="481" Height="324" UpdateRate="100" TimeFrame="1" Color="FF000000" />
      <ViewControl SimulationObject="Filter" Type="Simple" X="1110" Y="369" />
      <ViewControl SimulationObject="Sine" Type="Trend" X="0" Y="0" Width="481" Height="324" UpdateRate="100" TimeFrame="1" Color="FFFF0000" />
      <ViewControl SimulationObject="Sine" Type="Simple" X="112" Y="324" />
    </Screen>
  </Screens>
</ProcessSimulator>



Связь с внешним миром



Предыстория
Когда, я только начал заниматься данным проектом пришлось решать несколько дилемм. Прежде всего, появилась мысль для создания симулятора использовать какой-нибудь SCADA пакет. Таким образом, уходила основная проблема коммуникаций. Доступный, на тот момент, для меня Siemens WinCC 6.2, имеет множество драйверов, в том числе, OPC и S7-PLCSim, которые были необходимы. Чуть позже, выяснилось, что реализовать то, что я задумал, на чистом WinCC, невозможно. Всё упиралось в контроль времени, например, хода задвижки от открытого до закрытого состояния. Следующая мысль была – использовать WinCC как контейнер для работы .Net элементов (.Net controls), в которых реализовать симуляцию.

Возможно дело в кривых руках, но заставить нормально работать .Net элемент в WinCC у меня не получилось. Интернет не помог, поэтому я решил отказаться от данной затеи. Может быть, в последней версии WinCC 7 всё работает, не выяснял.

После того, как появилась возможность использовать готовую библиотеку OPC клиента, решил написать свой велосипед, к которому потом добавил внутренние переменные, связь с S7-PLCSim, и наконец, функционал OPC сервера, для доступа ко всем переменным извне.


В приведённом выше примере используются только внутренние переменные. Само собой, это не имеет смысла, так как основная задумка симулятора в том, чтобы имитировать для программы ПЛК работу с реальным оборудованием.

Если на компьютере, где запущен симулятор, установлена программа Siemens SIMATIC S7-PLCSim (V5.4 + SP4), можно легко организовать запись результата нашего преобразования синусоиды в любую (допустимую) память симулятора ПЛК.

Сначала необходимо подключиться (Items->Connect S7 PLCSim). В окне выбора экземпляра можно выбрать цифру от 1 до 8. Это номер экземпляра S7-PLCSim (пишется в заголовке окна) к которому мы хотим подключиться. Последняя версия позволяет на одной машине запустить одновременно восемь экземпляров.

Создадим новую переменную “FilterS7” с типом S7 PLCSim и укажем адрес, например, MD0 (Memory Type = M; Data Type = S7_DoubleWord; Byte = 0; Floating Point = true). Теперь достаточно в объектах поменять переменную “Filter” на “FilterS7”.

С подключением по OPC всё в том же порядке. Подключаемся к внешнему серверу (Items->Connect OPC), создаём переменные, из доступных на сервере, и используем их в объектах симуляции.

Необходимо учитывать, что в отличие от внутренних переменных, переменные S7 PLCSim и OPC не всегда доступны и для чтения и для записи. Например, в область Q симулятора ПЛК записать ни чего нельзя, так как это область выходов.

Последнее, о чём хотел бы упомянуть, это то, что симулятор предоставляет доступ ко всем своим переменным в качестве OPC сервера. Все переменные видны под своими именами и доступны любому OPC клиенту, например MatrikonOPC Explorer.

Данный функционал можно использовать, например, для связи S7PLCSim со SCADA или другим пакетом который поддерживает OPC. Конечно, предварительно, все переменные должны быть добавлены в Process Simulator.

Заключение


Мне удалось сделать инструмент, который очень помогает мне в работе. Возможно, он пригодиться кому-нибудь ещё, буду рад отзывам и даже bug report-ам :). Если вам чего-то не хватает – напишите. Возможно, в перерывах между бесконечными командировками у меня будет время немного расширить библиотеку объектов доступных в программе.
Tags:
Hubs:
+13
Comments 6
Comments Comments 6

Articles