August 29, 2018

Вебинар. Интеграция полевых устройств HART через FDT

Не прошло и полугода с момента, когда 10 апреля этого года Бенджамин Брунц и Лауриц Ветцель провели вебинар о подключении устройств HART через FDT. В том числе был небольшой практикум, где в живую показали "как это работает".

Незаметно для нас всё это встроено в TwinCAT. Вы с этим могли встречаться, если были замешаны в перерабатывающей промышленности (Processing Industry, это где одни вещества превращают в другие, а не где рабочие обязаны работать сверх нормы). Как раз в этой промышленности активно используются полевые устройства HART. Небольшую вводную я давал в описании синих, холодных и многобезопасных модулей.

HART — Highway Adressable Remote Transducer. Аналоговый сигнал 4..20мА (рекомендация NAMUR NE43). Широко используется в перерабатывающей промышленности. Позволяет совмещать аналог и цифру, то есть одновременно передавать пропорциональный аналоговый сигнал (амплитуда) и транслировать дискретные цифровые данные (с помощью FSK = Frequency Shift Keying) полнодуплексно и в обе стороны.

DTM — Device Type Manager. Чем-то похож на драйвер устройства. Обеспечивает двусторонний обмен данными между полевыми устройствами (датчиками там всякими) и ПЛК. Он же отвечает за конфигурацию устройства.

FDT — Field Device Tool. Определяет интерфейс и обеспечивает общение между DTM и прикладным уровнем программного обеспечения.

Для работы понадобится TwinCAT 3 build 4022 или новее. Для более старых версий TwinCAT необходимо установить HART-плагин, который можно получить, обратившись в тех. поддержку Бекхофф; ключевые слова: FDT контейнер + Beckhoff ComDTM (PACTware).

Преимущества:
  • Можно использовать существующие кабели рассчитанные на 4..20мА.
  • Двусторонняя, полнодуплексная связь устройств.
  • Возможность простого конфигурирования устройства через DTM.
  • Диагностика устройства и расширенная информация поступающая от устройства (если поддерживает).

Недостатки:
  • Требует дополнительных усилий на изучение.
  • Требуется дополнительное оборудование: соответствующее полевое устройство + модуль расширения (terminal).
  • Низкоскоростная передача: 500-800 миллисекунд на цикл. Правда скорость здесь не особо важна, так как главная цель — это целостность данных и полноценный контроль за целостностью данных.

Бекхофф официально входит в FDT группу, поэтому в прайс Бекхоффа входят EtherCAT терминалы с поддержкой HART: EL3182, ELX3181, ELX4181. Если интересуетесь подробностями HART — почитайте документацию этих модулей, там много интересного. Например, кратко, что из себя представляет модуль EL3182 — это 2-канальный аналоговый вход, 16 бит, ±107%, NAMUR NE43, HART; опционально настраивается через HART-плагин, есть FDT контейнер при использовании Beckhoff ComDTM.


Практическая часть


После сканирования шины и боксов, HART терминалы будут выглядеть как обычные модули расширения (например, как обычные аналоговые входа). Различия проявятся в расширенных настройках — в правой части экрана появятся две новые закладки HART и FDT.


FDT позволяет привязать DTM-драйвер устройства к заданному каналу. HART — настроить настройки. Все выполняется очень просто: сканирование, перетаскивание, выбор параметров из списка. Сложности это не представляет, и хорошо показано на вебинаре (знание английского не требуется).

Циклическая и синхронная передача данных в ПЛК-программу настраивается в закладке HART. Мы можем выбрать активный канал (Active Channel), затем перейти во вкладку отображения измеряемых величин (Measured Values Display) и поставить там галку —  циклически передавать данные (Cyclic Process Data). В конфигурации, рядом с веткой Ch.1 AI Inputs, получим новую длинную ветвь — Ch.1 HART Inputs. Эта ветка содержит данные, получаемые от HART-датчика. Линкуем эти параметры с переменными ПЛК-задачи и циклически получаем свежие данные. В описании терминала EL3182, есть раздел Measured values, где все это описано.

Если есть желание получать данные от случая к случаю, то есть асинхронно и когда захочется — существует сервис ADS: IdxGrp = 0xF302; IdxOffs = код команды. Эта информация также есть в описании модуля расширения, в разделе Acyclic services.


Полный вебинар на английском языке: Integration of HART field devices via FDT.

August 23, 2018

Очень длинный кейс

В продолжение темы экономии на абстракциях, я заметил необычное поведение отладчика TwinCAT 2 при пошаговом прохождении оператора CASE.

Программист, используя оператор CASE, заменяет спагетти из "ифов" (IF's) на красивую таблицу из структурированных блоков кода. Выполнение этих блоков зависит от значения одной единственной переменной. Именно так мы строим машину состояний, конечный автомат или просто разбиваем большой код на небольшие куски кода.

Мы ожидаем, что при входе в кейс, будет прочитано значение переменной, затем будет выполнен шаг с номером, хранящимся в этой переменной. Под дебагером же происходит нечто странное: происходит вход в кейс, а затем отладчик проходит по каждому шагу кейса, пока не дойдет до заданного шага и здесь, ожидаемо, начнет выполнять блок кода. Остальные участки кода он слава богу игнорирует, в том числе и те, что лежат условно "ниже" текущего шага (по тексту программы), но что если у нас кейс состоит из 1000 шагов? А то, что он проскачет по всем шагам, пока не достигнет нужного, и мы уже видели, что даже пустые вызовы подпрограмм, налагают дополнительную нагрузку на процессор ПЛК. Это поведение стоит проверить.

(* Я вынес анимированные иллюстрации в конец поста, чтобы не мелькали перед глазами. *)

Так как замерять производительность нужно на большом количестве вызовов и написать пару тысяч строк мне сложно и просто лень, да и времени на это жалко — я накидал программу на C#, которая генерирует другую программу, но уже на ST. Остается только скопировать из сгенерированного текстового файла output.txt код ST-программы, а затем вставить его в проект.

using System.IO;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var writer = new StreamWriter("output.txt")) {
                writer.WriteLine("CASE i OF");

                for(int i = 0; i < 65000; i++)
                    writer.WriteLine($"{i}: c := c + 1;");

                writer.WriteLine("END_CASE");
            }
        }
    }
}


Выводы

  1. Количество шагов оператора CASE никак не влияют на производительность. Во время выполнения программы, при входе в оператор CASE, управление сразу же передают в текущий шаг кейса. Никаких посторонних прыжков не происходит. Это исключительно заморочки режима отладки, причем, только в TwinCAT 2.
  2. Не надо писать гигантские кейсы из 100500 шагов — отладчик падает, а проект собирается два часа.
  3. В TwinCAT 3 все нормально и с отладкой, и с производительность. Отладчик непосредственно переходит к исполняемому в данный момент шагу, игнорируя остальные блоки. Но отладчик все равно не переваривает 100500 шагов кейса и падает.
  4. Удобство языка программирования ST в его текстовости и в том, что вы можете генерировать его программы с помощью других языков программирования: C#, VB, Python, JS, ... автоматизируя рутинные действия. Похожий прием я использовал в проекте Tc3_PcSpeaker для работы со звуковой-пищалкой x86 контроллеров. Только программа была написана на питоне. 

TwinCAT 2




TwinCAT 3



August 22, 2018

Экономия на абстракциях

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

Что если разбивка на множество подпрограмм привносит дополнительную нагрузку на процессор ПЛК и снижает быстродействие?

Быстродействие важно и любые телодвижения в сторону абстракций, приводят к увеличению количества вызовов подпрограмм (функций, методов, других кусков кода). А ведь нам нужно успеть уложиться в миллисекунды базового времени (Base Time, в первую очередь), и только затем улучшить сопровождаемость (когда-нибудь потом, но лучше прямо сейчас).

Спагетти код ругают: все-таки трудно ориентироваться в бесконечной портянке неструктурированного кода непонятного назначения. Поэтому я хочу знать — имеет смысл экономить на абстракциях или я могу разбивать код на сколько угодно удобных мне частей?

Итак, исследуем влияние количества вызовов подпрограмм на загруженность процессора. Проверять TwinCAT 2 мне не очень-то и хотелось, но я проверил и не пожалел.

PROGRAM MAIN
VAR
    i: UINT;
END_VAR

FOR i := 0 TO 65000 DO
    PRG2();
END_FOR

(* ********************** *)

PROGRAM PRG2
VAR
    c : UINT;
END_VAR

;
// c := c + 1;
// c := c + 1;

Основная программа MAIN каждый цикл совершает ряд вызовов подпрограммы PRG2. Количество вызовов определяется количеством итераций цикла. Их максимальное число равно 65000 — как в примере выше. Так как нас в основном интересует нагрузка от ветвления программы, то подпрограмма PRG2 пустая. Точнее, она состоит из единственного оператора точка-с-запятой (;). Чтобы не было совсем банально, я разбавил подпрограмму парой строк со счетчиком (и это было правильным решением).

По вертикали (Y) — нагрузка процессора в процентах, по горизонтали (X) — количество вызовов подпрограммы за время одного цикла:
Ничего необычного: чем больше вызовов, тем больше нагрузка на процессор. При этом нужно учесть два факта: первый — подпрограмма PRG2 ничего не делает и нагрузка растет только за счет количества передачи управления из одной программы в другую; и второй — тест синтетический и в реальных ситуациях такого количеств вызовов за один цикл не бывает.

Теперь проверим TwinCAT 3:
...и он показывает более линейную зависимость.

Теперь об аномалиях второй версии TwinCAT. В подпрограмме PRG2 я убрал пустой оператор ";" и раскомментировал одну строку со счетчиком. Зафиксировал количество вызовов на числе 65000. Замерил нагрузку. Раскомментировал вторую строчку со счетчиком (теперь их две). Замерил нагрузку и впал в задумчивость.

Итак, внимание! С одной строкой счетчика, нагрузка составила 72%. С двумя строками счетчика — 43%. Больше кода — меньше нагрузка! Объяснить такое поведение можно только работой некоего оптимизирующего звена в компиляторе кода, но такая неявность, явно вводит в заблуждение. Я немедленно проверил то же самое в TwinCAT 3...

...и он по прежнему оказался линеен и предсказуем: 1 строка — 56%, две строки — 58%.


Выводы

  1. Вызовы подпрограмм добавляют нагрузку на процессор.
  2. Я решил, что не буду обращать на эту нагрузку внимание, так как ощутимой она становится только при каких-то очень больших числах вызовов за время одного цикла. Даже для CX8xxx серии.
  3. TwinCAT 3 стабильнее и предсказуемей, чем его вторая реализация.
  4. Upd. 29 мая 2019 года, TwinCAT 2 v2.11.2302 странности с "меньше кода - больше нагрузка" не наблюдаются. Возможно был баг с выравниванием переменных в памяти. 


P.S.: посчитаем вызовы


А как вообще прикинуть — сколько вызовов происходит в проекте?

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

Для начала, открываем проект и экспортируем его в единый файл экспорта *.exp. Затем, открываем EXP-файл в текстовом редакторе (Notepad++) и автозаменой удаляем все вхождения следующих строк: (*STRING(, IF (. Файл в итоге будет испорчен, но для нас важнее исключить посторонние элементы. Остается сделать глобальный поиск символа открывающей скобки (, который традиционно используется при вызове подпрограмм. Как вариант, поискать закрывающие скобки, а точнее конец вызова функции, что-то похожее на );

Количество вхождений искомых символов, будет приблизительно равно количеству вызовов функций и подпрограмм. Часть совпадений придется на математические формулы, но их не так много. В то же время часть вызовов мы все равно не обнаружим, так как они скрыты внутри библиотек и в других трудно доступных местах. Одно компенсирует другое, и в среднем получится более-менее правдоподобно.


На картинке выше подсвечены не все скобки, но они учтены и подсчитаны правильно.

В итоге, я нашел 560 вызовов с открывающими скобками и 433 с закрывающими. Это в два раза меньше 1000 (1-2% CPU, как в пустой программе) и совсем далеко от 65000 (56-58% CPU). Еще раз, это максимум, который можно вызвать, но который никогда не будет достигнут: программа разбита на шаги (машина состояний), в каждом из которых выполняется лишь небольшая часть всей технологической программы.

August 10, 2018

Виртуальная машина с Windows CE и TwinCAT

Когда под рукой нет "реального железа" — можно обойтись локальным рантаймом. Когда нужен рантайм под Windows CE (WEC, Embedded Compact) — мы идем за виртуальной машиной. В моем случае — это Oracle VirtualBox. Бекхофф же предлагает образ под Virtual PC и нам это только на руку.

Качаем с сайта Бекхофф образ виртуальной машины для Virtual PC. Образов несколько, они предназначены для различных версий TwinCAT (2, 3) и WEC (6, 7). Я выбрал под WEC 7 с TwinCAT 2.11R3 NCI(!): Beckhoff_VPC_WEC7_HPS_v502a_TC211R3_B2247.zip. Полагаю, что для остальных версий ситуация схожая (upd 24 авг 2018: да, для WEC7 TC3.1 всё аналогично).
Сходу образ не запустился. Пришлось обновить Virtual Box версии 5.1 до последней версии 5.2.16. Возможно проблема исключительно в моих системных настройках, но я предупредил.
Какие настройки Virtual Box необходимо задать? Кстати, не обязательно такие, просто эти проверены, работают и взяты из оригинального образа Virtual PC:

Name (произвольное название): WinCE 7
Operationg System: Other Windows (32-bit)
Base Memory: 512 MB
Video Memory: 8MB (virtualbox будет ругаться, что этого мало для полноэкранного режима)
Network Adapter →
        Attached to: Bridget Adapter
        Advanced → Adapter Type: Intel PRO/1000 MT Desktop (82540em)

Главное правильно задайте сетевой адаптер. В дальнейшем он понадобится для связи с внешним миром, а в качестве жесткого диска используйте .vhd файл из образа виртуальной машины. Выберите при создании пункт: Use an existing virtual hard disk file.


После этого у вас должна нормально запуститься виртуальная машина с Windows Embedded Compact 7 и TwinCAT 2.11R3 NCI. Но(!) без сетевой карты. Печально, что инженеры подложили нам двух свиней: во первых, это специальная версия TwinCAT, которая будет останавливаться каждый сутки (TC Daily Drop); во вторых, не установлены сетевые драйверы, которые позволили бы нам подключить периферию, да и вообще хоть как-то использовать виртуальную машину в качестве ПЛК. Исправляем.

Нам необходимы драйверы сетевой карты Intel e1000 для Windows CE. Вообще, это редкость и всяческий раритет, но гугл в помощь. Я нашел и выкачал комплект драйверов на сайте HPC:Factor в виде единого файла e1000ce5leg.exe. В целях безопасности, мы не будем запускать исполнимые .exe файлы из неизвестных источников (нельзя собирать грибы в незнакомом лесу), а просто распакуем его с помощью 7zip. Ссылка на перепакованный архив в подвале статьи. На поверку, это оказался обычный .cab архив, завернутый в MSZip. Осталось закинуть полученные файла на виртуальную машину.

И теперь мы воспользуемся преимуществом, подаренным нам виртуальной машиной Virtual PC. Образ жесткого диска от виртуальной машины, мы просто подключим к обычной настольной операционке. У меня Windows 10 Home, как дела с более старыми версиями — подсказать не могу.
7-zip версии 18.05 умеет распаковывать .vhd архивы, но не умеет добавлять в них файлы. Возможно другие архиваторы умеют это делать, и вам не понадобятся следующие телодвижения с подключением виртуальных дисков.
Итак, нам нужно "Управление дисками". Правой кнопкой по меню Пуск → "Управление дисками" или правой же кнопкой по Этому компьютеру → "Управление", а дальше разберетесь.

В управление дисками выбрать "Действие" → присоединить виртуальный жесткий диск.

Создайте в корне диска какой-нибудь каталог (temp, net, drivers, произвольные латинские буквы-цифры... главное, название не забудьте). Пусть будет netdrivers. Скопируйте в этот каталог содержимое архива e1000ce5leg.zip. В списке дисков кликните правой кнопкой по виртуальному диску → отключить виртуальный диск. Теперь можно запускать виртуальную машину.

Доступа к сети все еще нет, но теперь у нас есть драйверы. Запустите проводник: Start → Run... → explorer. Перейдите в каталог drivers, выделите все файлы → Edit-Copy. Перейдите в \Hard Disk\System → Edit-Paste. Запустите файл \Hard Disk\System\E1000CE5.reg для активации драйверов. Система предложит перезагрузиться → соглашайтесь.

После загрузки вы получите доступ к локальной сети и все соответствующие плюшки. Можно доустановить недостающие компоненты (подключая-отключая виртуальный жесткий диск), а затем сделать экспорт виртуальной машины и поделиться уже готовым образом через File → Export Appliance... Каталог с распакованными файлами драйвера (netdrivers в моем примере) можно удалить.



Архив с драйверами доступен здесь: e1000ce5leg.zip [77 Кб]