May 30, 2019

Обмен данными через ADS между ПЛК-задачами

Уже во втором твинкате была библиотека TcDataExchange и, соответственно сейчас в третьем есть библиотека DataAccess → Tc2_DataExchange. Основное назначение — она умеет отправлять переменные ПЛК через ADS. При этом одновременно можно использовать как имя переменной, так и пару индекс-смещение. Получается этакий комбайн, умеющий все и сразу, и сильно упрощающий жизнь и движение между ПЛК задачами.
И там действительно есть полезный набор функций, но(!) нужно быть осторожным. В случае регулярной или частой пересылки данных, возможны непредвиденные нагрузки на систему. Причина в том, что внутри комбайна прячется комбайнер CASE с рядом скрытых от разработчика действий, которые активно перемалывают системные ресурсы.

При работе через ADS, для начала необходимо получить дескриптор переменной или, иначе говоря, занять какой-то системный ресурс. А раз занять, то по окончании требуется вернуть или сохранить до следующей обработки той же переменной. В библиотеках ADS для программирования вне системы реального времени уже предусмотрено кэширование хэндлов: существует скрытая внутренняя таблица, позволяющая системе не заниматься слишком частым занятием-освобождением хэндлов и соответственно ресурсов.

Функции чтения-записи FB_ReadAdsSymByName и FB_WriteAdsSymByName в зависимости от значения параметра eComMode также могут запоминать и повторно использовать полученный дескриптор, но только один. Соответственно, экземпляр функция с параметром eComMode = E_AdsComMode.eAdsComModeFastCom должен работать только с одной переменной. Это обязательно необходимо учесть, так как по умолчанию используется безопасный режим eComMode : E_AdsComMode := eAdsComModeSecureCom, который фактически становится опасным из-за постоянного передергивания ресурсов системы.

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

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

May 28, 2019

Git Ignore для TwinCAT

TwinCAT 3 благополучно переполз на текстовые форматы данных. Никаких больше бинарников невнятного формата, которые не сравнить и не выгрузить в системы контроля версий. GitHub, GitLab, Bitbucket и много других бесплатных онлайн сервисов. Правда, если вы не боитесь выгружать свои совершенные проекты в облако.



В интернете и без меня много ютуб учебников, поэтому кратко:
  • git — распределённая система управления версиями. Есть и другие.
  • github и ко — крупнейший веб-сервис для хостинга IT-проектов и совместной разработки.
Не равно: git != github или git <> github.
Когда вы наконец-то освоите последовательность действий: init, commit, push, итд., приходит время для нюансов. Например, не имеет смысл держать в проекте студийный мусор и бинарные библиотеки. Для исключений используется файл .gitignore и он автоматически исключает из проекта ненужное. Или то, что разработчик сочтет ненужным.

Для проектов Visual Studio есть стандартно-универсальный VisualStudio.gitignore. Там есть все, кроме части, ответственной за TwinCAT проекты. Я добавил.


.gitignore для TwinCAT проектов


Файл .gitignore можно отредактировать в любом текстовом редакторе. Добавляете в конец файла:

# Beckhoff TwinCAT3 projects
*.~u
_Libraries/
_Boot/
_CompileInfo/
*.tclrs
*.tclrq
*.tpy
*.tmc
*.bak


И получаете незамусоренный проект. Возможно, вы захотите что-то оставить или что-то удалить. Есть с чего начать: Source Control.

May 24, 2019

Контроль состояния сервотерминалов EL7201

Что если силовое питание не подано, ось не активна и вдруг разорвать сигнал обратной связи? Что если оторвать провод силового питания? Как будет реагировать NC? Возможно ли вообще отследить такой тип аварий и как сбросить ошибку? Копну глубже в контроль состояния компактных сервоусилителей EL72xx, и начну с обратной связи.
Изображение: Beckhoff Automation

Обратная связь


Сервоось недееспособна, если отсутствует сигнал обратной связи. Необходимо регулярно вызывать функцию Axis.ReadStatus(), чтобы понять, что с ней происходит. В момент потери сигнала будет выставлен флаг Axis.Status.DriveDeviceError.

Так как работа сервомотора без обратной связи невозможна, самостоятельно этот флаг не сбросится и не "рассосется". После устранения причины аварии, ошибку нужно будет сбросить с помощью стандартной функции MC_Reset. Но это всё на случай, если ПЛК и сервомодули нельзя обесточивать на время ремонта, обслуживания или замены оборудования. Безопаснее выключить и включить снова.


Силовое питание


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

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

Слово состояния сервомодуля (6010:10 — Statusword) содержит два бита, отвечающие за ошибки и аварии. Это бит #3 Fault и бит #7 Warning: стр. 143, Index 6010 DRV Inputs. Поле Fault отвечает за аварии. Этот бит транслируется подсистемой NC в поле состояния оси DriveDeviceError. Именно его мы отслеживали в главе про обратную связь. Потеря обратной связи — это авария, но(!) отсутствие силового питания — это еще не авария, а просто ситуация требующая особого внимания. Поэтому — Achtung, т. е. Warning.

Флаг Warning также транслируется в NC, но он не доступен через параметры состояния NC-оси. Причина этого для меня не понятна, но я попробую добраться и до этого флага.

Начнем с того, что Statusword передается в PDO сервомодуля. Затем, оно автоматически линкуется с NC параметрами Axis.Drive.Inputs.In.[nState1..nState2], попутно разбиваясь на старший и младший байты. И всё. Далее эти байты используются где-то внутри подсистемы NC и недоступны разработчику.

Чтобы получить доступ к слову состояния, можно непосредственно постучаться в сервомодуль, то есть прочитать параметры через функции CANopen. Можно и по другому. Если немного погуглить (а это более качественный способ поиска информации по справочной системе), то обнаруживается интересная таблица с индексами:

TwinCAT Connectivity → ADS-Device-Documentation → ADS Interface NC → Specification "Index group" for NC ( ID [0x01...0xFF] ) → Specification Drive → "Index offset" specification for cyclic drive process data (Index group 0x7300 + ID).

Из таблицы не совсем понятно зачем всё это необходимо, но судя по названиям...


Читаем другие параметры


Читать будем через ADS. Для чтения параметров, перечисленных в "таблице", мы воспользуемся функцией ADSREAD из библиотеки системных функций Tc2_System. Для запуска функции понадобятся:
  • NETID — пустая строка, если читаем с того же локального ПЛК.
  • PORT — это стандартный порт NC-Task SAF = 501.
  • IDXGRP — индекс группы из таблицы = 0x7300 + ID, где ID - это номер оси NC; нумерация осей начинается с единицы, то есть первая ось получит индекс = 16#7301.
  • IDXOFFS — смещение из таблицы = 16#80.

В таблице доступ к параметру 16#80 помечен как "Write", но все относительно и зависит от точки зрения, поэтому я буду из него "Read". Осталось решить куда прочитать данные. По идее необходима некая структура данных, но есть ли она в стандартных библиотеках мне не известно, поэтому я создал парочку своих собственных DUT. Выглядят они почти как в той самой таблице из справочной системы:

TYPE EL7201_DriveInfoEx :
STRUCT
    nInData1    : DINT;
    nInData2    : DINT;
    StatusWord  : EL7201_StatusWord; // Axis.Drive.Inputs.In.[nState1..nState2]
    nStatus3    : BYTE;
    nStatus4    : BYTE;
    // optional : extended drive info, 40 bytes
    nInData3    : DINT;
    nInData4    : DINT;
    nInData5    : DINT;
    nInData6    : DINT;
    nStatus5    : BYTE;
    nStatus6    : BYTE;
    nStatus7    : BYTE;
    nStatus8    : BYTE;
    Reserved1   : DINT;
    Reserved2   : DINT;
END_STRUCT
END_TYPE

Для удобства использования сразу же разбиваю слово состояния на структуру из битовых полей:

TYPE EL7201_StatusWord  :
STRUCT
    ReadyToSwitchOn     : BIT;
    SwitchedOn          : BIT;
    OperationEnable     : BIT;
    Fault               : BIT;
    Reserved4           : BIT;
    QuickStop           : BIT; // inverse: true when switched off
    SwitchedOnDisabled  : BIT;
    Warning             : BIT; // Ex.: raise when Power Supply lost
    Reserved8           : BIT;
    Reserved9           : BIT;
    TxPDOToggle         : BIT; // selection/deselection via 0x8010:01
    InternalLimitActive : BIT;
    TargetValueIgnored  : BIT;
    Reserved13          : BIT;
    Reserved14          : BIT;
    Reserved15          : BIT;
END_STRUCT
END_TYPE

В таблице есть указание что структура EL7201_DriveInfoEx может быть длинной как в 12 байт, так и расширенная, длинною в 40 байт. В моем случае необходима расширенная структура. Остается прочитать данные:

axis               : AXIS_REF;
axDriveStatus      : EL7201_DriveInfoEx;
AdsReadDriveStatus : ADSREAD;

[...]

AdsReadDriveStatus(
    NETID    := '', 
    PORT     := 501,
    IDXGRP   := 16#7300 + axis.NcToPlc.AxisId,
    IDXOFFS  := 16#80,
    LEN      := SIZEOF(axDriveStatus), 
    DESTADDR := ADR(axDriveStatus), 
    READ     := TRUE);
 
IF NOT AdsReadDriveStatus.Busy THEN
    AdsReadDriveStatus(READ := FALSE);
END_IF


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


Уровень силового питания


Значение силового питания (12-50 Вольт) можно прочитать напрямую из сервомодуля. Адрес сервомодуля можно получить с помощью функции MC_ReadDriveAddress и структуры ST_DriveAddress:

Изображение: Beckhoff Automation

Значение уровня постоянно обновляется в параметре CoE 9010:12 — DC link voltage. Значение дается в милливольтах, поэтому 24 вольтам будет соответствовать значение 23932. Почему не 24000? Потому что — не точно.

Следующий кусок кода прочитает значение напряжения прямо из сервомодуля:

dcLinkValue : DINT;
AdsReadCoE  : ADSREAD;

[...]

AdsReadCoE(
    NETID    := '169.254.23.39.4.1', 
    PORT     := 1002,
    IDXGRP   := 16#F302,
    IDXOFFS  := 16#90100012,
    LEN      := SIZEOF(dcLinkValue), 
    DESTADDR := ADR(dcLinkValue), 
    READ     := TRUE);
 
IF NOT AdsReadCoE.Busy THEN
    AdsReadCoE(READ := FALSE);
END_IF


NETID — адрес EtherCAT мастера.
PORT — номер порта устройства, в данном случае — это сервомодуль EL7201.
IDXGRP — индекс группы сервиса ADS, отвечающего за работу с CANopen SDO.
IDXOFFS — индекс и смещение регистра CAN. Для упрощения в примере выше индекс не вычисляется, так как в шестнадцатеричной системе его легко сформировать вручную 9010-0012. Если интересно, чуть более подробно написано в посте Работа с CANopen из C# программы.

Когда пример готов, подключаю цифровой осциллограф к переменной dcLinkValue и получаю график "зарядки-разрядки". Здесь питание контроллера и силовое питание сервомодулей подается от одного 24 вольтового блока питания. Поэтому "потолок" на графике ~ 24000, а сервомодуль работает с половинной мощностью:

Синяя кривая на графике показывает, что модуль разряжается долго или по крайней мере не мгновенно. Стоит учесть это, если вдруг захочется контролировать уровень напряжения из программы.

February 28, 2019

Профили сервотерминалов MDP и DS

В среде Бекхофф есть два вида сервотерминалов EL72x1-000x. С одной стороны они совершенно одинаковые по электромеханическим параметрам; с другой стороны, они отличаются: во-первых, цифрой в модели, во-вторых, названием профиля: MDP742 или DS402. Что выбрать? Ответ можно найти в статье Profile MDP 742 or DS 402.

Профили относятся к стандарту CANopen. Для полной ясности, EL72x1 внутри себя сидит на шине CAN, данные которой транслируются дальше на шину EtherCAT. Профили определяют номера индексов/смещений, порядок/структуру параметров, а также ряд других свойств словарей объектов CAN. Оба профиля и MDP742, и DS402 содержат одинаковые наборы параметров, отличающиеся индексами и названиями параметров. Я бы такому заявлению про "одинаковость" сильно не доверял, поэтому и полез разбираться.

Вывод простой: оба профиля имеют одинаковый набор функций, но различаются доступом к ним, поэтому и работать с ними придется по разному. Правда TwinCAT сильно скрывает это. Сконфигурировав дерево проекта, на уровне переменных программы ПЛК, вы с параметрами CAN случайно уже не столкнетесь, только намеренно.

DS402 относится к стандарту IEC61800-7-200 (CiA402). Функционально — это то же самое и полностью совместимое вплоть до машины состояния. Поэтому единственная причина его наличия в прайслисте — это совместимость с чужим оборудованием или для работы в составе чужого оборудования.

Если же говорить о выборе между профилями, MDP742 (Modular Device Profile) — это стандартное представление набора параметров CoE объектов EtherCAT модулей Бекхофф. Именно с таким профилем терминалы поставляются с завода Бекхофф. Получается, что если специальные требования совместимости с DS402 отсутствуют и разработчику без разницы какие там параметры и в каком они порядке следуют, нужно выбирать MDP742. Иначе говоря, если вы не работаете напрямую с параметрами CAN — выбирайте MDP. Если вам все-равно — выбирайте MDP.

Вообще, профиль можно сменить, загрузив в сервотерминал другой профиль. После замены профиля необходимо обновить EEPROM, ESI и не забыть про двигатели. Так как описание объектов CoE и операционный образ (process data) профилей различаются, то необходимо также заменить в проекте XML профиль двигателей.

February 27, 2019

Быстрый стоп и как линковать биты и булы

Есть кнопка Пуск, а есть Стоп. На минуту представьте себе большую и толстую центрифугу, которая разгоняется в течении нескольких минут. Как прервать выполнение команды NC, когда команда все еще пытается, но еще не достигла результата?

Вообще, в большинство команд NC PTP встроен параметр BufferMode, позволяющий стыковать два задания или, проще говоря, создать плавный переход от одного задания к другому. В том числе и для MC_Halt (останов движения), который может быть вторым в цепочке заданий. К сожалению, проблему это не решает — момент остановки по прежнему непредсказуем, а выглядеть это будет, как заторможенная реакция системы на реакцию кнопки "стоп".

Если же вы смелы и отважны, и вас не пугает останов с последующим сбросом ошибки, то можно кое-как выкрутиться и с помощью MC_Halt, но как-то это всё не красиво и хочется нормального решения без ошибок. Давайте определимся с заданием — необходимо остановить "толстую" центрифугу из любого состояния: не мгновенный стоп за кратчайший промежуток времени, а просто быстрый останов без ошибок, из любого состояния системы, независимо от выполняемой команды и прочее, прочее, прочее.


Fast Axis Stop


Ситуация, в которой останов происходит как можно ранее и желательно быстрее, описана в справочной системе Fast Axis Stop. Правда, в статье не хватает описания некоторых возможных побочных эффектов. А всё потому, что их просто — нет! Всё, дальше можно не читать.

В настройках NC оси есть специальный параметр nState4 типа USINT (он же UINT8 или просто BYTE). Седьмой бит (7..0) этого параметра управляет быстрым остановом. Система у нас дискретная, поэтому перед работой можно и нужно определиться с типом управляющего сигнала: передний фронт, задний, активный/неактивный, отключено/не отключено, ... то есть как и когда будем "дергать" бит управления. Все это задается в настройках NC оси:


Быстрая остановка происходит из любого состояния системы. Не важно что там сейчас, в любое время разгона, торможения, работы, останова и вообще в любых других ситуациях. Работает останов даже в режиме электронного редуктора, когда происходит быстрый останов мастера, но(!) не ведомого: ведомый полностью игнорирует функцию быстрого останова и продолжает полностью подчиняться мастеру.


Линковка переменных


В программе необходимо завести переменную ввода-вывода, через которую будем управлять функцией быстрого останова. Для управления достаточно одноразрядной переменной типа BOOL. Затем связываем переменную с параметром NC: FastStop AT %Q* : BOOL;

Здесь появляется первый нюанс — два бита восьмиразрядного параметра nState4 уже связаны с какими-то там переменными. Вы не увидите этого, пока работаете с виртуальными осями и мгновенно столкнетесь, когда будете работать на реальном "железе". Для проверки я буду использовать дополнительные задачи Additional Tasks или просто Tasks в модном TwinCAT 3, но все-равно учту, что линковать нужно несколько переменных одновременно.

В ветке проекта Tasks, создаем дополнительную задачу с произвольным названием: Add new item... → TwinCAT Task With Image. Под веткой Output переменных создаем переменную типа BOOL или BIT. Позже попробуйте поэкспериментировать с многоразрядными типами типа BYTE или WORD: это тоже интересно в плане частичной линковки переменных и параметров.


Теперь нужно добраться до параметра nState4: Motion → SAF → Axes → Axis N → Drive → Inputs → In. Добавляем линк к трём параметрам сразу, и помогает нам в этом клавиша Ctrl, и ряд параметров фильтра переменных (Show Variables и Show Variable Types):


Главная проблема в том, что параметр nState4 типа BYTE (USINT, UINT8) и, следовательно, он восьмиразрядный, а нам от него нужен всего-лишь один бит. Поэтому при линковке параметров разного типа на экран вываливается специальный диалог Variable Type Mismatch (несовпадение типа переменной) и начинается диалог с разработчиком:


  • Size — размер переменной в битах.
  • Offset — смещение в битах от начала переменной (31..23..15..7..0). Обратите внимание, счет ведется от нуля, поэтому в счете фигурируют не восьмёрки, а семёрки.

  • Own Variable — переменная или параметр, в которую разработчик ткнул мышкой. Есть разница с какой переменной начинать.
  • Linked Variable — переменная, к которой мы будем приклеиваться.
  • Overlapped — перекрытие или сколько бит от переменной нужно взять. Не обязательно пристёгивать все разряды, можно ограничиться каким-то определенным числом разрядов.

nState4 — параметр длиной 8 разрядов. Для линковки с переменной FastStop нам нужен только 7-й разряд, соответственно, для WcState — необходим нулевой разряд, для InputToggle — первый бит. Что и отражено в колонке Offset строки Own Variable.

Картинка с Main.fastStop (правый-нижний угол) дана для наглядности. Именно так, оно бы выглядело в реальной программе.


Стоп с блокировкой


Для останова движения в NC PTP есть два вида функциональных блоков MC_Halt и MC_Stop. Первый просто понижает текущую скорость до нуля; второй же, блокирует все операции на оси до тех пор, пока ось не остановит движение, а затем будет ждать пока разработчик не снимет блокировку. Это надежно, но непонятно, как в этом случае поведет себя функция быстрого останова.

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


Горб на правом склоне второго графика, живописно демонстрирует процесс быстрого останова.


Уголок антиквара


В TwinCAT 2 всё аналогично:


October 29, 2018

Введение PowerShell для TwinCAT

В TwinCAT 3 есть штуки, позволяющие автоматизировать рутинные последовательности действий. Дальше как в известной байке с реддита: "Этично ли будет не сообщать работодателю, что я автоматизировал свою работу"?

В текущем проекте мне необходимо периодически перегружать TwinCAT: остановить сервис TwinCAT, запустить сервис TwinCAT, перевести локальный рантайм в режим конфигурации или в рабочий режим. Вообще, не проблема написать все это на C#, но здесь уместнее будет использовать командную строку и какой-нибудь скриптовый язык программирования. Под Linux есть bash и Python. Под Windows они тоже есть, но это противоестественно для его экосистемы. Поэтому будем экологичны — воспользуемся PowerShell 'ом.

PowerShell строится вокруг инфраструктуры .NET как и C#. Он также имеет удобный доступ к COM-объектам, а на них построены множество сервисов TwinCAT. Дальше хвалить не буду, начнем чистую практику. В идеале, я хочу получить кнопку, которая будет перезапускать TwinCAT в один клик. См. иллюстрацию справа →

Перезагрузить TwinCAT несложно — достаточно остановить и стартовать заново системный сервис TcSysSrv. Сделать это можно вручную или через командную строку. Сначала, стоп: net stop TcSysSrv; затем, рестарт — net start TcSysSrv, но после этого TwinCAT запустится в режиме стоп (красная иконка). Чтобы перевести его в какой‑либо рабочий или полурабочий конфигурационный режим, необходимо использовать функции ADS.API. К сожалению, через командную строку они не доступны: нужно запускать System Manager или XAE.


Инсталляция


Запустить консоль PowerShell можно из меню Пуск: клавиша  с окошком Windows → открывается меню Пуск, начинаете набирать powershe... Там же можно запустить встроенный в Windows редактор PowerShell ISE. При запуске от имени администратора, появляется больше возможностей, но и больше дыр в защите вашего ПК.


Чтобы перезапустить сервис TwinCAT можно использовать команду Restart-Service TcSysSrv -Force. Ключ -Force избавит от лишних вопросов. Причем, заметьте, не просто остановить или запустить, а одной командой остановить и перезапустить. Уже удобнее, чем было, но есть одно "но"(!) — перед запуском необходимо разобраться с правами на запуск командлетов-скриптов PowerShell: Get-ExecutionPolicy и Set-ExecutionPolicy. Погуглите по русски или читайте справочную систему на английском-немецком Check the Powershell Cmdlet Execution policy, это домашнее задание.

Теперь осталось разобраться с функциями ADS.API. Для них есть NuGet-PowerShell пакет TcXaeMgmt. Перед использованием, его необходимо установить командой Install-Module -Name TcXaeMgmt, соглашаясь [Y] со всем, что предложат:


Windows PowerShell
(C) Корпорация Майкрософт (Microsoft Corporation). Все права защищены.

PS C:\Windows\system32> Install-Module -Name TcXaeMgmt

Для продолжения требуется поставщик NuGet
Для взаимодействия с репозиториями на основе NuGet модулю PowerShellGet требуется версия поставщика NuGet "2.8.5.201"
или более новая. Поставщик NuGet должен быть доступен в "C:\Program Files (x86)\PackageManagement\ProviderAssemblies"
или "C:\Users\username\AppData\Local\PackageManagement\ProviderAssemblies". Поставщик NuGet можно также установить,
выполнив команду "Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force". Вы хотите, чтобы модуль
PowerShellGet установил и импортировал поставщик NuGet прямо сейчас?
[Y] Да - Y  [N] Нет - N  [S] Приостановить - S  [?] Справка (значением по умолчанию является "Y"):Y

Ненадежный репозиторий
Идет установка модулей из ненадежного репозитория. Если вы доверяете этому репозиторию, измените его значение
InstallationPolicy, запустив командлет Set-PSRepository. Вы действительно хотите установить модули из "PSGallery"?
[Y] Да - Y  [A] Да для всех - A  [N] Нет - N  [L] Нет для всех - L  [S] Приостановить - S  [?] Справка
(значением по умолчанию является "N"):Y
PS C:\Windows\system32>



Командлет


Для переключения режимов работы TwinCAT из PowerShell, используется команда: Set-AdsState -State Config.

Вообще, после установки пакета TcXaeMgmt, вы сможете делать из командой строки практически всё что угодно:... конкретнее, читайте в about_TcXaeMgmt.help.txt. Только помните — так как вы будете влиять на системные сервисы и другие важные штуки Windows, от вас часто будут требовать права Администратора. Их тоже можно выдавать автоматически из скрипта PowerShell.

Итоговый командлет, большую часть которого составляет выделение админских прав, выгляди так:

if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).
        IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) 
{
    Start-Process
        -WindowStyle Hidden
        -Verb RunAs
        powershell.exe "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"";
    exit
}

Restart-Service TcSysSrv -Force
Set-AdsState -State Config -Force


Сохраняем скрипт в файл с расширением .ps1. Файл кладем в C:\TwinCAT\3.1\Target\StartMenuAdmin. Здесь же делаем на него ссылку-ярлык и изменяем имя ярлыка на удобное нам название. Оно тут же появится в системном меню TwinCAT, картинка которого была в начале поста.


October 8, 2018

Сторожевой пес для CX8090

Сторожевой таймер (или watchdog, или вотчдог для краткости в дальнейшем) нужен в критических ситуациях. В щите сбора данных — может быть, а вот в станке, я ставлю его необходимость под сомнение. Тем не менее, вопросы типа "боюсь, что программа зависнет" или более самоуверенные — "что, если зависнет контроллер", задают постоянно. В CX8090 такая функция есть.

И была. Изначально. А вот библиотека для работы с ним, появилась не сразу.
Попробуйте не использовать сторожевой таймер в своих проектах. Неправильное применение сторожевого таймера может привести к бесконечной перезагрузке ПЛК, что в результате приведет к выходу из строя контроллера. Это печально само по себе, и вдвойне грустно, потому что случай не гарантийный.

Все испытания проводились на новом ПЛК CX8090, произведенном 2 августа 2018 года.

История библиотеки


Я беру с полки CD-диск c официальным дистрибутивом TwinCAT 2 за 2013 год и открываю исходный код библиотеки TcSystemCX80xx.lib, предназначенной специально для CX80xx. Функции вотчдога в ней еще нет.

Диск за 2014 год — функции все еще нет. Версия библиотеки 1.0.2. Да собственно чего тянуть, функция была добавлена только в версии 1.0.3, но на диск попасть не успела или просто тестировалась какое-то время. Вот комментарий разработчика:

2014/02/17 | 1.0.3 | V2.11.0 (Build 2239) | ICH | adding F_CX80xxSetWatchdog


Мне, к сожалению, не удалось найти старые библиотеки на сайте Бекхофф, хотя я бы с удовольствием покопался в истории промышленного софтостроения. Так что, конкретно указать на версию TwinCAT я не могу. Начиная с версии TwinCAT 2.11 (build 2249) внезапно появляется версия 1.0.6, которая до сих пор лежит на сайте Бекхофф. И это не самая последняя версия, так как с последними дистрибутивами TwinCAT 2 распространяется версия библиотеки 1.0.7.

За все это время функциональный блок вотчдога не изменялся ни разу.


Как спустить сторожевого пса


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

FUNCTION F_CX80xxSetWatchdog : BYTE
VAR_INPUT
    tTimeOut : TIME; (* Watchdog TimeOut Time *)
    bEnable  : BOOL; (* Enable / Disable Watchdog *)
END_VAR

Это функция. Она не требует инстанциирования как функциональный блок. Просто регулярно вызывайте ее с (bEnable = TRUE), когда нужна функция сторожа; или вызовите блок единственный раз с (bEnable = FALSE), если нужно отключить таймер. Другими словами, блок требует регулярного вызова каждый цикл или по крайней мере не реже, чем это задано в параметре tTimeOut.
По идее функции должны быть сосредоточены сами в себе и ни коим образом не влиять на глобальное окружение. В данном же случае, такое применение уместно, так как: (а) функция всегда существует в единственном экземпляре и всегда требует ввода двух обязательных параметров; (б) другие программные единицы TwinCAT 2 так не умеют. В TwinCAT 3 мы могли бы попробовать шаблон Singleton.
На входе функции всего два параметра: (1) время реакции tTimeOut типа TIME, который может принимать значения в диапазоне от 500 миллисекунд (не меньше) до 127 секунд (не больше); (2) и параметр активациия/деактивации таймера bEnable типа BOOL. Куда уж проще.

После вызова функции, с целью активации сторожевого таймера (bEnable = TRUE), сразу же, в этом же цикле, начиная со следующей строчки после вызова функции, начинается контроль выполнения программы. Теперь, если программа зависнет и цикл не завершится в отведенное ему время, или если за цикл произойдет критическая ошибка, или сработает точка останова(!), если вы просто забудете регулярно вызывать функцию вотчдога — таймер начнет свой отсчет...

...и через заданное время tTimeOut — ПЛК перезагрузится.

После перезагрузки, ПЛК сделает вид, что ничего не произошло. Он будет считать, что загрузился штатным способом и запустится так, как было задано в его текущей системной конфигурации. Если у вас активирован загрузочный проект (boot project), то ПЛК-программа стартует автоматически, запустит вотчдог и, если ваша программа продолжит зависать, то контроллеру наступит бесконечный уроборос: зависание, перезагрузка, автозагрузка программы, зависание...

Из-за постоянной круговерти с загрузкой-переагрузкой, ПЛК рано или поздно может выйти из строя. Почему? Например, потому что скидывает на флэшку PERSISTENT данные или остаются незакрытыми файловые операции операционной системы, или любая другая причина. Не используйте вотчдог, пишите программы аккуратно.

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

(* return enable state *)
nRetVal.0           := bEnabled;
nRetVal.1           := bMinWDTimeAct;
nRetVal.2           := bMaxWDTimeAct;
F_CX80xxSetWatchdog := nRetVal;

Далее номера битов в байте выхода:
  • бит 0 — установлен, когда таймер запущен.
  • бит 1 — установлен, когда время таймаута меньше или равно 500 миллисекундам.
  • бит 2 — установлен, когда время таймаута больше или равно 127 секунд 500 мс.


Что внутри будильника?


Заглянем в недра тундры. Вотчдог хранит время таймаута в 500 миллисекундных интервалах. 127 секунд и 500 миллисекунд или 127 500 — это как раз 255 раз по 500. Затем это значение сдвигается на восемь бит "влево" в старший байт, а в младшем байте выставляются биты разрешения или запрещения.

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

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

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


Практика


Практиковаться будем на трех примерах ниже, каждый из которых так или иначе останавливает исполнение программы. Конкретнее, это точка останова, деление на ноль и бесконечный цикл. Набрасываете требуемый номер шага в переменную state и через 3 секунды получаете реакцию вотчдога на соответствующую критическую ситуацию:

PROGRAM MAIN
VAR
    state, cnt : INT;
    z : int := -1;
END_VAR

(*[...]*)

F_CX80xxSetWatchdog(tTimeOut := T#3s, bEnable := TRUE);

CASE state OF
0:
    F_CX8090_LED_WD(eMode := eLED_RED_OFF);
    F_CX8090_LED_ERR(eMode := eLED_RED_OFF);

    z := z + 1;
    state := 10;
    
10: (* breakpoint *)
    ;
    
20: (* zero division *)
    z := state / z;
    
30: (* inf. loop *)
    F_CX8090_LED_WD(eMode := eLED_RED_FLASHING_200ms);
    WHILE TRUE DO
        ;
    END_WHILE
    F_CX8090_LED_WD(eMode := eLED_RED_OFF);
END_CASE

cnt := cnt + 1;


Помигаем светодиодом на прощанье


На корпусе CX8090 есть индикатор WD (=Watchdog), который в документации заявлен как не используемый в прошивке для CX8080 / CX809x. Тем не менее у нас есть механизм для управления этими лампами: функция F_CX8090_LED_WD управляет светодиодом WD, F_CX8090_LED_ERR — управляет светодиодом ERR (для ошибок). Доступны несколько режимов свечения и мигания с различной скважностью: покопайтесь в типах данных библиотеки.

Касательно отключения индикаторов, то оба индикатора двухцветные. Поэтому не важно какой цвет индикатора выключать: красный (eLED_RED_OFF) или зеленый (eLED_GREEN_OFF), отключается сразу весь светодиод, то есть оба цвета свечения пропадают одновременно.

Вызвать функцию достаточно один раз. Отработает она в том же цикле и даже раньше его завершения. В дальнейшем, работой индикаторов управляют какие-то внутренние цепи "железа", совершенно отвязанные от TwinCAT, поэтому блоки не требуют вызова каждый цикл. А вообще, можно сделать так:

F_CX8090_LED_WD(eMode := eLED_RED_FLASHING_200ms);

WHILE itHaveToContinueThen DO
    ; (* что-то делаем в цикле *)
END_WHILE

F_CX8090_LED_WD(eMode := eLED_RED_OFF);


И еще раз, постарайтесь не использовать сторожевой таймер.

October 4, 2018

Веб-конфигуратор для CX8090

Последнее время Бекхофф встраивает в свои контроллеры веб-интерфейс для начальной настройки ПЛК. Это хорошо, потому что через ADS-роутер можно кое-как выяснить IP и AmsNetId -адреса ПЛК, но для запуска FTP-сервера или сервиса удаленного доступа — этого уже мало. В умный по сути коплер CX8090 просто некуда воткнуть святую троицу монитор-клавиатура-мышь. Разъемы для них не предусмотрены и не поддерживаются. Надо изгаляться... надо было, так как теперь все это делается через веб-интерфейс. По крайней мере первый шаг, который, как известно, чего-то-там для человечества.

Находим контроллер через TC2 System Manager или роутер TwinCAT 3: иконка TC3 в системном лотке (traybar) → Router → Edit Routes → TwinCAT Static Routes. В колонке Address видим IP-адресс ПЛК. Открываем любимый браузер. Для разнообразия, и чтобы показать, что никакой разницы нет, я взял нелюбимый в народе Edge.

Вводим в адресную строку: IP-адресс-ПЛК/config. Логин по умолчанию: webguest, пароль: 1.



Есть на что посмотреть и с чем поработать. И сразу же, чтобы не забыть потом, включаем удаленный доступ для работы с ПЛК через CERHost. А дальше уже не проблема запустить FTP-сервер, "залить" русские шрифты и поправить настройки в реестре Windows CE.


September 2, 2018

Работа с CAM-профилями

Есть ли возможность корректировать CAM-профиль (таблицу кулачкового взаимодействия) на лету? Например, когда ПЛК-программа подставляет новые точки в профиль или изменяет уже имеющийся профиль прямо в процессе отработки.

Да, можно. В процессе отработки профиля, можно изменять не только координаты точек траектории, но и скорость, ускорение и рывок (jerk). Подставлять новые или удалять уже имеющиеся точки — нельзя, но можно исключить имеющиеся точки из траектории. В дальнейшем система будет игнорировать их, что будет выглядеть, как будто мы их удалили.
MC_MotionFunctionPoint .PointType : MC_MotionPointType := MOTIONPOINTTYPE_IGNORE
По мере необходимости, скрытые точки можно реактивировать заново, что можно расценивать, как добавление точек в траекторию. Необходимо заранее предусмотреть максимально необходимое число точек.

А что, если в ПЛК уже сидит какая-то сконфигурированная CAM-таблица, но мы хотим иметь возможность корректировать ее, то есть изменять профиль, причем, не залезая в конфигуратор и не реактивируя конфиг? Например, необходимо организовать полный доступ к CAM-таблице через интерфейс пользователя.

Новые профили (CAM-таблицы) можно создавать из ПЛК-программы в любое время, в том числе и во время исполнения текущей CAM-таблицы. Можно создавать таблицы в редакторе профилей, встраивать их как образцы в конфигурацию, а затем в рантайме редактировать их из ПЛК-программы. Также можно "на лету" переключаться между профилями/таблицами или запускать их друг за другом, выстраивая сложную траекторию из отдельных кусков простых.

Вообще, система не различает таблицы созданные в редакторе и созданные вручную из ПЛК-программы. Система различает таблицы по их номеру CamTableID : MC_CAM_ID. По сути, этот номер просто синоним для числа типа UDINT, который отражает номер таблицы среди всех доступных таблиц. Системе всё равно, где была создана таблица.

TYPE
    MC_CAM_ID : UDINT;
END_TYPE


Возникает вопрос, а нужен ли вообще редактор Cam Design Tools или можно как-нить обойтись и сэкономить? Как-нить можно, но ругают его в основном за стоимость.

Редактор крут и наворочен. Позволяет одним махом нарисовать и отредактировать не только кривую траекторию, но и задать скорости, ускорения, рывки в узловых точках, по сути доступен полный набор производных движения. Траекторию можно сглаживать всякими полиномами пятой степени и загружать их одним кликом, вместе с остальным проектом. Стоит это все 4,5К евро, что для многих ставит жирный крест на рисовании траекторий кулачковых механизмов, отправляя их в пешее путешествии за таблицами в Экселе.

Если нет лицензии на редактор, то таблицы из CAM-дезигнера будут доступны только до первого закрытия студии или PLC Control'а. После повторного открытия конфигурации или проекта, этих таблиц в проекте уже не будет — они пропадут. Аналогично, без лицензии эти таблицы не захотят выгружаться в контроллер. Рисовать можно, грабить караваны координат и тащить их в казематы Экселя — можно, сохранять проект для потомков или в ПЛК-задачу — нельзя.

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


Создание CAM-таблиц


Если вы создаете CAM-таблицу вручную, то вам необходима функция MC_CamTableSelect. Данные, предоставленные вами в функцию, передаются в NC, где создается новая CAM-таблица. Эта функция не нужна, если вы создали таблицу в дизайнере: можно сразу переходить к MC_CamIn.

Чего нет в документации, так это фразы, что функция CamTableSelect по сути создает новую таблицу. Если же таблица с таким номером уже существует, то старая таблица будет удалена и на ее месте создана новая. Вот кусок, отвечающий за этот процесс (находится в библиотеке TcMC2_Camming). Для удобства, я выделил ключевые слова:

(* delete old table (possibly existing)*)
STATE_INTERNAL_DELETE :
    fbAdsWrite( NETID   := TcMcGlobal.NCNETID_TCMC_CAM,
                PORT    := TcMcGlobal.NCPORT_TCNCCAMMING_TABLEFUNCTION,
                IDXGRP  := TcMcGlobal.Table.Functions.IDXGRP + CamTableID,
                IDXOFFS := TcMcGlobal.Table.Functions.IDXOFFS.DELETETAB,
                LEN     := 0,
                SRCADDR := 0,
                WRITE   := TRUE,
                TMOUT   := TcMcGlobal.tADSTimeOut);

    IF NOT fbAdsWrite.BUSY THEN
        IF NOT fbAdsWrite.ERR THEN
            (*next step*)
            iStateInternal := STATE_INTERNAL_CREATE;
        ELSE

Видно, что библиотека пытается сначала удалить таблицу с заданным CamTableID и только затем переходит к созданию новой таблицы. Точки из старой таблицы пропадают в никуда.


Активация профиля и наоборот


MC_CamIn — занимается сцеплением подчиненной оси с мастер-осью, а прослойкой между ними служит таблица с индексом CamTableID. Можно рассматривать эту таблицу как электронный редуктор с программным коэффициентом передачи, который по ходу работы будет извлекаться из данной таблицы.

После того, как мастер будет запущен, подчиненный начнет ползать в соответствии с CAM-таблицей. Причем, если позиция подчиненного не соответствует позиции в таблице, то он выполнит максимально быстрый скачок в требуемую позицию. Здесь требуется большая осторожность!

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

Если оси двигались до процесса расцепления, то после расцепки они сохранят свою скорость движения, но уже как независимые оси (например, остановка одной оси никак не повлияет на другую ось). Такое поведение очень похоже на поведение блока MC_MoveVelocity.


Последовательность выполнения функций


Для примера, простая последовательность действий выглядит как-то так:
  1. MC_CamTableSelect( CamTableID
  2. MC_SetCamOnlineChangeMode
  3. MC_Power (TRUE, Master
  4. MC_Power (TRUE, Slave
  5. MC_CamIn
  6. MC_MoveVelocity( Master
  7. ...технологический процесс
  8. MC_CamOut
  9. MC_Halt( Master
  10. MC_Halt( Slave
  11. MC_Power (FALSE, Master
  12. MC_Power (FALSE, Slave


Изменение параметров движения


Прежде, чем изменять параметры движения, то есть вносить изменения в CAM-таблицу, нужно договориться с системой — как и когда вы будете это делать. Для переговоров используется функция MC_SetCamOnlineChangeMode. Достаточно выполнить ее один раз с необходимым набором параметров. Например, задать ActivationMode = MC_CAMACTIVATION_ASSOONASPOSSIBLE и данные будут применяться на лету, как можно быстрее и в самый безопасный момент движения.

Перед изменением можно прочитать существующий параметры (если они уже загружены) с помощью функции MC_ReadMotionFunction. Функция прочитает все данные таблицы, либо только заданное количество, а если вы зададите больше, чем есть на самом деле, то не страшно — функция прочитает только то, что будет доступно, ошибок не будет.

Для доступа к данным отдельной конкретной точки, используется функция MC_ReadMotionFunctionPoint. Данные содержат не только координаты, но и много другой полезной информации. Начнем с PointIndex — это порядковый номер (индекс) точки в траектории. Индексы положительны, начинаются с 1 и далее идут без разрывов, с единичным шагом: 1, 2, 3, 4, 5, ... и никаких нулей для стартового индекса.

Индексы последовательны, но порядок обхода точек — произвольный. За это отвечает поле RelIndexNextPoint — это относительный индекс следующей вычисляемой точки траектории. По умолчанию, он равен 1, что означает — выполнять по порядку следования точек. Для последней точки в траектории относительный индекс = 0. Изменять относительный индекс "на лету" нельзя, так как этот параметр важен для предварительного обсчета траектории.

И последнее, но не менее важное поле, FunctionType. Оно задает функцию движения для формирования траектории и расчета промежуточных точек. По умолчанию, используется MOTIONFUNCTYPE_POLYNOM5_MM — гладкая траектория с интерполяцией полиномами 5-й степени. В дизайнере этому соответствует функция Automatic. Типу же Synchron соответствуют сплайны первой степени MOTIONFUNCTYPE_POLYNOM1.

Учитывайте, что при использовании MOTIONFUNCTYPE_POLYNOM5_MM (или Automatic) необходимы как минимум три активные точки в куске траектории, то есть нужна сама точка и по одной точке с каждой стороны от нее. Итого, три точки в куске. Точка считается активной, если она не игнорируется PointType <> MOTIONPOINTTYPE_IGNORE. Это важно, если вы используете механизм, описанный в самом начале поста.


Практикум


Я создал замкнутый, повторяющийся каждые 360° профиль. Для простоты, траектория разбита пятью точками на четыре кусочно-линейные интервала движения. Точка №5 совпадает с точкой №1. Не забывайте, вы ставите точки, а траекторию между ними рассчитывает система.



На самом деле, я сначала нарисовал график в дизайнере, а затем вручную перетащил данные в ПЛК-программу (см. Приложение ниже). В итоге, для подчиненной оси получился следующий график:


До позиции 21000 подчиненный отрабатывает заданный в CAM-таблице график. Я отрезал на картинке "пики" позиций, чтобы не загромождать иллюстрацию.

После 21000 я программно, с помощью MC_WriteMotionFunctionPoint, ставлю в игнор точку 2: следите как изменился график от 21000 до 23500. Начиная с 23500 (вторая зеленая линия), я ставлю в игнор точку 3: с 23500 по 25000 работают только точки 1, 4, 5.
Учтите, что непосредственно точки я не удаляю, а всего-лишь изменяю их тип на PointType := MOTIONPOINTTYPE_IGNORE.
После позиции 25000 я возвращаю сначала точку 2, а где-то ближе к 28000 и точку 3. Затем все повторяется. Прогоните в голове эту последовательность несколько раз и всё встанет на свои места.


Приложение


PROGRAM MAIN
VAR
    AxMaster         : AXIS_REF;
    AxSlave          : AXIS_REF;
    camTable2_Id     : MC_CAM_ID := 2;
    camTable2        : MC_CAM_REF;
    camTable2_Points : ARRAY[1..10000] OF MC_MotionFunctionPoint;
    
[...]

camTable2.ArraySize   := SIZEOF(camTable2_Points);
camTable2.pArray      := ADR(camTable2_Points);
camTable2.TableType   := MC_TABLETYPE_MOTIONFUNCTION;
camTable2.NoOfColumns := 1;
camTable2.NoOfRows    := pointsNumber;

FOR i := 1 TO PointsNumber DO
    camTable2_Points[i].PointIndex := i;
END_FOR

camTable2_Points[1].PointType         := MOTIONPOINTTYPE_MOTION;
camTable2_Points[1].FunctionType      := MOTIONFUNCTYPE_POLYNOM1; // 5_MM;
camTable2_Points[1].RelIndexNextPoint := 1;
camTable2_Points[1].MasterPos         := 0.0;
camTable2_Points[1].SlavePos          := 0.0;

camTable2_Points[2].PointType         := MOTIONPOINTTYPE_MOTION;
camTable2_Points[2].FunctionType      := MOTIONFUNCTYPE_POLYNOM1; //5_MM;
camTable2_Points[2].RelIndexNextPoint := 1;
camTable2_Points[2].MasterPos         := 100.0;
camTable2_Points[2].SlavePos          := 10.0;

camTable2_Points[3].PointType         := MOTIONPOINTTYPE_MOTION;
camTable2_Points[3].FunctionType      := MOTIONFUNCTYPE_POLYNOM1; //5_MM;
camTable2_Points[3].RelIndexNextPoint := 1;
camTable2_Points[3].MasterPos         := 150.0;
camTable2_Points[3].SlavePos          := 100.0;

camTable2_Points[4].PointType         := MOTIONPOINTTYPE_MOTION;
camTable2_Points[4].FunctionType      := MOTIONFUNCTYPE_POLYNOM1; //5_MM;
camTable2_Points[4].RelIndexNextPoint := 1;
camTable2_Points[4].MasterPos         := 250.0;
camTable2_Points[4].SlavePos          := 10.0;

camTable2_Points[5].PointType         := MOTIONPOINTTYPE_MOTION;
camTable2_Points[5].FunctionType      := MOTIONFUNCTYPE_POLYNOM1; //5_MM;
camTable2_Points[5].RelIndexNextPoint := 0; // последняя точка в траектории
camTable2_Points[5].MasterPos         := 360.0;
camTable2_Points[5].SlavePos          := 0.0;


На самом деле, красивее и проще это сделать через массивы или, что еще лучше, через чтение .csv файлов, содержащие данные CAM-таблицы.

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.