March 29, 2017

EtherCAT Device Protocol

EtherCAT Device Protocol используется только для обмена данными между подчиненным устройством и мастером.


Фрейм


Фрейм целиком обрабатывается аппаратно с помощью специального микрочипа EtherCAT Slave Controller (ESC). Поэтому учитываются только фреймы с EtherType = 0x088A4 и EtherCAT Type = 1 (см. EtherCAT Automation Protocol).

Изображение: Beckhoff Automation GmbH (EtherCAT Slave Controller v2.2 2014-07-07)

EtherCAT телеграмма состоит из нескольких EtherCAT датаграмм, которые, в свою очередь, состоят из заголовка (header), данных (data) и 16-разрядного счетчика работоспособности WC (WkC, Working Counter).
Каждый подчиненный, выполнивший EtherCAT команду (чтение или запись — не важно), увеличивает значение счетчика WC на единицу или больше, в зависимости от типа команды. Получая пакет обратно, мастер знает сколько у него подчиненных и может сравнить ожидаемое значение счетчика с актуальным значением. Это позволяет системе определить полноценность исполнения EtherCAT команды.
Для представления данных датаграммы (Data) используется Mailbox Protocol. Кроме родного протокола ADS и его пакетов, в эту структуру можно уложить пакеты другого протокола, достигая трансляции вообще любых протоколов через EhterCAT. Например: CANOpen, SERCOS, PROFIbus и т. п.

Наиболее интересным будет заголовок EtherCAT датаграммы (Datagram Header). Он состоит из команды (Cmd) и адреса данных (Address). Адрес может быть автоинкрементальным, фиксированным (физическим) или логическим. Команда EtherCAT диктует, что делать с данными, расположенными по указанному адресу. Кроме этих двух полей, в заголовке расположены другая полезная информация:
  • Index (индекс) — номер датаграммы, для контроля за дублированием или повторением датаграмм.
  • Length (длина) — длина данных в датаграмме.
  • R — зарезервирован.
  • C (флаг циркуляции) — в случае обрыва соединения с мастером, фрейм может начать бегать по кольцу из подчиненных. Это не корректно.
  • M (последняя ли датаграмма?) — последняя ли это датаграмма в EtherCAT-фрейме.
  • IRQ (EtherCAT Event Request) — это поле необходимо для уведомления мастера о событиях, произошедших на подчиненных. Неизвестно кто из подчиненных непосредственно был инициатором события, поэтому программное обеспечение мастера должно самостоятельно разобраться с этим.


Команды


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

Действия:
  • NOP — нет операции, подчиненный игнорирует эту команду.
  • ..RD — записать в датаграмму считанные модулем данные (например, модуль дискретных входов).
  • ..WR — запись данные из датаграммы в память по заданному адресу (например, де/активация выходов модуля дискретных выходов).
  • ..RW — обмен данными датаграмма-память.

Адресация:
  • AP.. — подчиненный наращивает автоинкрементальный адрес и что-то делает, если автоадрес получился равным нулю.
  • FP.. — что-то сделать, если адрес в датаграмме соответствует одному из сконфигурированных адресов подчиненного.
  • L.. — что-то сделать, если адрес в датаграмме соответствует логическому адресу FMMU-области (см. ниже).
  • B.. — все подчиненные наращивают позицию.

Примечания:
  • BRD — подчиненные помещают в датаграмму логическое ИЛИ между данными из памяти и данными из датаграммы.
  • BRW — обычно не используется (аналогично команде BRD только данные помещаются в память).
  • ARMW — подчиненный наращивает автоинкрементальный адрес и помещает в датаграмму  прочитанные данные, если адрес получился равен нулю. Иначе подчиненный сохраняет данные датаграммы в свою память. Например, эта команда используется для DC: прочитал "часы" на "эталоне" и передал дальше для всех подчиненных, которые сохранили его.
  • FRMW — если адрес в датаграмме соответствует одному из сконфигурированных адресов подчиненного, то помещает в датаграмму прочитанные данные. Иначе подчиненный сохраняет полученные данные в свою память.

Упрощенно используется два вида адресации:
  • Автоинкрементальная (Auto Inc Addr) — используется для идентификации физического (реального) положения устройства на шине EhterCAT. Он наращивается автоматически и необходим для автоматического назначение адреса для каждого подчиненного шины. Первый подчиненный получает автоматический адрес = 0 (0x0000); второй подчиненный — автоадрес = 0xFFFF, третий = 0xFFFE.
  • Фиксированный адрес (EtherCAT Addr) — нужен для работы с модулем как с отдельным устройством (например, через CoE, CAN over EtherCAT). Если рассматривать внешнюю адресацию AmsNetId, то фиксированный адрес соответствует номеру порта устройства на шине (AmsNetId-подчиненного = AmsNetId-мастера).


Отображение


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

FMMU (Fieldbus Memory Management Unit). Эти модули находятся между соответствующим SyncManager (SMx) и физическим уровнем. Задача FMMU в отражении локального адресного пространства подчиненных модулей на глобальное адресное пространство мастер-контроллера и наоборот. Другими словами — FMMU транслирует и раскидывает данные из логического операционного образа (фрейма, датаграммы) в физическую память и обратно. FMMU умеет работать вплоть до битовых полей.

Изображение: EtherCAT Technology Group (ETG.2200 V2.1.4)


SyncManager


Обеспечивает согласованность передачи данных, синхронизирует их, предотвращая одновременный доступ к памяти контроллера (DPRAM). Всего SyncManager может быть до 16 штук. Они могут работать в двух режимах:

Режим почтового ящика (Mailbox):
  • Один буфер с режимом установки связи (handshake).
  • Защита от переполнения буфера.
  • Передающая сторона должна записать в буфер, перед тем как принимающая сторона сможет читать буфер.
  • Принимающая сторона должна "вычитать" буфер, перед тем как передающая сторона сможет записать буфер.
  • Используется для обмена данными "от случая к случаю" (иногда, нерегулярно) требуемых в данный момент данных.
  • Стандартный способ для обмена параметрами данных, диагностики, конфигурации рабочего образа данных.
  • Полнодуплексный.
  • Подчиненный может инициировать передачу.
  • Доступен уже из состояния Pre-Operational (Pre-OP).
  • Позволяет реализовать множество протоколов:
    • EoE (Ethernet через EtherCAT) — туннель Ethernet через EtherCAT.
    • CoE (CANopen через EtherCAT) — для передачи словарей объектов (словарей PDO)
    • FoE (Передача файлов через EtherCAT) — передача файлов, например, для перепрошивки устройства.
    • SoE (SERCOS через EtherCAT) — доступ к параметрам сервоусилителей.
    • VoE (уникальный протокол разработчика (Vendor specific) — разработчик может самостоятельно разработать свой собственный протокол.

Буферизированный режим (Buffered):
  • Менеджер синхронизации с тремя буферами гарантирует целостность данных и доступ к новым данным в произвольный момент времени.
  • Всегда доступный буфер для записи.
  • Буфер чтения всегда заполнен гарантированно целостными данными (за исключением момент старта, то есть перед первой записью в буфер чтения).
  • Используется для циклического обмена заранее сформированного списка данных (PDO).

Стандартное распределение SyncManager:
  • Mailbox
    • SM0 — Mailbox-вывод.
    • SM1 — Mailbox-ввод.
    • SM2 — PDO-вывод.
    • SM3 — PDO-ввод.
  • Buffered
    • SM0 — PDO-вывод (или PDO-ввод, если отсутствует вывод).
    • SM1 — PDO-ввод.

PDO всегда точно умещается в буфере соответствующего SyncManager. Поэтому размер PDO всегда ограничен.


PDO и SDO


PDO в терминах TwinCAT — это заранее подготовленный пакет с описанием набора параметров которые будут передаваться или синхронизироваться совместно. PDO заимствованы из CANOpen.

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

Синхронизация обмена данными (ключевое слово SYNC) — необязательная, но целесообразная подсистема. При использовании данной подсистемы в сети существует генератор синхросообщений, периодически передающий высокоприоритетное сообщение SYNC. После появления в сети такого сообщения все синхронизируемые устройства производят обмен данными в течение заданного временного интервала(окно синхронного обмена данными). Коллизии (одновременная передача данных двумя и более устройствами) разрешаются на уровне физического уровня протокола CAN. Словарь объектов содержит перекрёстные ссылки откуда какие данные взять, и какие куда положить. Таким образом приложения не занимаются самостоятельно сбором данных — просто с точки зрения приложения в определённых переменных периодически оказываются свежие данные.

При обмене PDO, с точки зрения приложения, всё происходит автоматически по определённым правилам, и приложение, не обращаясь к сетевым примитивам, получает данные из переменных, как будто бы данные появляются внутри этого самого прибора. Для получения данных по принципу SDO приложение должно при помощи сетевых сервисов запросить данные у другого устройства, и только потом, получив ответ, использовать данные для работы. Поэтому основу обмена данными следует строить на PDO-обмене. К сожалению имеются ограничения на размер данных(8 байт для PDO, но можно использовать несколько таких PDO).

SDO стоит использовать только по необходимости. При SDO обмене данными, устройство, к которому обратились с запросом на получение или запись(dowload/upload) данных, называется SDO сервером, а устройство которое инициировало обмен — называется клиентом. В зависимости от объема передаваемых данных, обмен производится по разным алгоритмам, и может быть не менее эффективен чем PDO обмен. SDO обмен допускает производить контроль безошибочности данных, что позволяет даже загрузку отдельных порций исполняемого кода.


PDO в TwinCAT


Зоны в закладках System Manager → Process Data:
  • Download
    • PDO Assignment — можно изменять список готовых PDO (добавлять или исключать объекты-пакеты).
    • PDO Configuration — можно изменять PDO (дополнять, убирать параметры в объект-пакет).
  • SyncManager — какие SM и какого типа доступны для данного устройства.
  • PDO Assignment — список PDO доступных для данного SyncManager и объектов которые включены для обмена. В зависимости от доступности, которое видно в зоне Download → PDO Assignment, это содержимое можно изменять.
  • PDO List — словарь PDO, доступных для данной железки.
    • Index — индекс PDO.
    • Size — размер в байтах.битах (через точку).
    • Name — Имя PDO. Если данный PDO закреплен за SyncManager, то данный PDO появится как параметр/переменная в дереве конфигурации контроллера с данным именем.
    • Flags.
    • F — неизменяемый PDO (Fixed), нельзя изменить состав данного PDO.
    • M — обязательный (Mandatory).
    • SM — номер SyncManager за которым закреплен данный PDO. Если не закреплен, то PDO не участвует в обмене данными.
    • SU — номер SyncUnit за которым закреплен данный PDO.
  • PDO Content — содержимое PDO (набор параметров которые будут передаваться или синхронизироваться совместно). В зависимости от доступности, которое видно в зоне Download → PDO Configuration, это содержимое можно изменять.

Полностью конфигурируемые PDO выглядят как:



Фиксированный набор PDO модуля дискретных сигналов:


  • Predefined PDO Assigment — заранее подготовленные профили или наборы PDO.
  • Load PDO info from device — выгрузить информацию о PDO из устройства, иначе будут использоваться локальные словари из каталога x:\TwinCAT\Io\EtherCAT.
  • Sync Unit Assigment... — распределить PDO по фреймам.

Подробнее о настройках можно прочитать в справочной системе EtherCAT Slave Device.

March 28, 2017

EtherCAT Automation Protocol

Пробежим по EtherCAT от транспортировки пакетов, до синхронизации на уровне переменных в размеченных областях памяти.


Транспорт


Данные EtherCAT передаются в виде пакетов, которые полностью совместимы с Ethernet-пакетами. Это означает, что EtherCAT-пакеты на физическом (PHY) и MAC-уровне построены так, что для Ethernet-сетей они выглядят как родные и устройства типа роутеров, свичей и хабов могут передавать их не задумываясь, наравне с остальными пакетами Ethernet. Если же необходимы более высокоуровневые фишки (типа IP-роутинга), то без проблем можно перейти на уровень выше и поместить фрейм EtherCAT в UDP/IP датаграмму.

Изображение: beckhoff.com


Фрейм


Упрощенно EtherCAT фрейм состоит из заголовка и данных, но сейчас нам будет интересен только заголовок (header), который в свою очередь состоит из поля длины и поля типа данных, который определяет протокол передачи данных внутри пакета:
  1. EtherCAT Device Protocol
    • Тип 1: данные EtherCAT.
  2. EtherCAT Automation Protocol (EAP)
    • Тип 4: Синхронный обмен операционным пакетом, PD (Process Data Communication).
    • Тип 5: Асинхронный обмен через "почтовые ящики" (Mailbox communication). Для асинхронного обмена доступен только протокол AoE (ADS over EtherCAT). Остальные протоколы, такие как CAN, SERCOS, PROFIBUS и т. п., передаются "внутри" его фреймов.

При непосредственном подключении EtherCAT-подчиненного к мастеру всегда используется EtherCAT Device Protocol (тип 1). Для этого на подчиненном устройстве устанавливается специальный микрочип — EtherCAT Slave Controller (ESC). Для остальных видов связи, в том числе и для построения сложных маршрутов, используется EtherCAT Automation Protocol (EAP, тип 4 или 5). Оба протокола могут использоваться как синхронно, так и асинхронно.

Подробнее о структуре фрейма EAP можно прочитать в справочной системе — EAP telegram structure.


Синхронный обмен


Ключевые слова — PD, PDO. Максимальная длина EtherCAT-телеграммы — 1472 байта. Синхронный обмен происходит с помощью следующей сетевой матрешки:

Изображение: beckhoff.com


Телеграмма состоит из заголовка Process Data Header (PDH) и одного или нескольких операционных наборов данных Process Data (PD). Заголовок содержит Publisher Id — уникальный идентификатор устройства отправителя (издатель, publisher) данных. Кроме него есть счетчик, нарастающий каждый цикл, и поле с общим количеством блоков, расположенных в PD.

Каждый операционный набор (PD, Process Data) состоит из одного или нескольких PDO. Каждый PDO состоит из заголовка PDO Header и одной или нескольких переменных PDO Variables. PDO Header содержит идентификатор переменной и ряд других полей: версия PDO, длина PDO, свежесть данных в 100 микросекундных интервалах.

Если асинхронный обмен может происходить в произвольные моменты времени, то синхронный обмен обязан происходить через равные промежутки времени, что достигается двумя способами:
  1. Pushed Data Exchange (способ проталкивания) — когда "издатель" (Publisher) отправляет PD в сеть через равные промежутки времени или при изменении своего состояния, а "подписчик" (Subscriber) принимает и обрабатывает. Таким образом работают "сетевый переменные" (Network Variables) в TwinCAT 2. За один раз отправляется и принимается один фрейм данных.
  2. Polled Data Exchange (опросный способ) — когда "клиент" отправляет запрос, в котором содержится его PD, а в ответ получает отдельный фрейм ответа с PD информацией сервера. Работает клиент-серверная архитектура, поэтому суммарно в общении участвуют два сетевых фрейма.
Здесь внезапно проявляется различие в работе протоколов EtherCAT Device Protocol и EAP, так как в случае с Device Protocol формируется один единственный большой пакет, в котором заранее предусмотрено место для данных подчиненных устройства. По мере прохождения пакета через кольцо EtherCAT-шины в эти пустые места EtherCAT-фрейма "на лету" будут вставляются данные подчиненного устройства. Это очень быстро.
В случае же с EAP можно достичь 10 миллисекундного отклика, если применяются свичи и гигабитный Ethernet; и 100мс отклик, в случае применения UDP/IP роутинга. Это медленнее, но более гибко и можно запускать через интернет. При этом не стоит забывать, что параллельно в сетях Ethernet могут общаться и другие устройства, что в свою очередь может привести к нарушении синхронности обмена сообщениями.

Pushed (1) способ передачи позволяет передаваться данные как на одно устройство, так и на группу устройств:
  • Unicast: один издатель — один подписчик.
  • Multicast: один издатель — группа заданных подписчиков.
  • Broadcast: один издатель — все доступные устройства.
Polled (2) способ передачи связывает только два конкретных устройства.


Триггеры


Отправка EAP телеграм основана на механизме триггеров: происходит какое-то событие и если условие выполнено — происходит отправка ответа. Не рекомендуется использовать одновременно несколько условий для срабатывания триггера, так как система не может гарантировать их взаимодействие между собой.

Существует пять различных условий для срабатывания триггеров:
  1. Poll Request Rx PD — пришла телеграмма опроса.
  2. Divider/Modulo — регулярная отправка. Divider — это множитель для времени цикла отправки, Modulo — множитель для ожидания перед первой отправкой (смещение первой телеграммы).
  3. Cycle Time — отправка через заданный интервал в микросекундах. Отношение интервала цикла передачи и длительность цикла задачи должны быть кратны целому числу.
  4. Изменение состояния по таймауту (Change of State, CoS) — отправка происходит при изменении значения переменной. Если значение не менялось в течении заданного времени (в миллисекундах), то происходит повторная отправка того же значения, что и в прошлый раз.
  5. Изменение состояния с гарантированным интервалом (Change of State, CoS) — отправка происходит при изменении значения переменной. Между отправкой старого и нового значений должно пройти не меньше заданного времени  (в миллисекундах).
В любом случае интервал или промежуток времени для условий должен быть равен или больше времени цикла задачи, опрашивающей устройство.
Подробнее о механизме триггеров — EAP Send Mechanism.


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


Ранее, в TwinCAT 2, такая модель называлась "сетевыми переменными" (Network Variables). В TwinCAT 3 ее назвали своим именем EtherCAT Automation Protocol. Так или иначе, в начале вы создаете несколько переменных базовых типов или даже структур, а затем TwinCAT автоматически транслирует их через Ethernet на другие устройства (контроллеры, ПЛК, и т. п). Тем самым вы получаете более-менее синхронные данные на обеих сторонах, без необходимости в написании кода для поддержки сетевой передачи данных программой.

Создание EAP-конфигурации:
  • Добавление EAP устройства.
  • Отправка переменных (источник данных).
  • Подписка на переменные.
  • Использование типов данных пользователя (структуры).

Конфигурирование EAP-устройства:
  • EAP устройство.
  • Издатель (Publisher Box).
  • Переменные издателя.
  • Подписчик  (Subscriber Box).
  • Переменные подписчика.
  • EAP между TwinCAT 2 и TC3 — обмен данными работает в обе стороны. Автоопределение сетевых переменных (Browse for Computer, Browse for File) работает только в одну сторону: TwinCAT 3 автоматически подхватывает переменные из TwinCAT 2.

March 20, 2017

Вебинар. Базы данных в приложениях TwinCAT 3

Данных стало много, данные важны, мы хотим еще больше еще более сложных данных. Желательно собрать их в облаке и анализировать в офисе за чашкой кофе или даже дома.

На прошлой неделе Паскаль Дресселгауз (Pascal Dresselhaus) рассказал и показал сервис, да-да, не сервер, а именно сервис для работы с базами данных: Database Server, Part 1: Database connectivity easily established with TwinCAT. Ссылка на видео вебинара где-то в конце поста.


Функциональная концепция


Все крутится вокруг TwinCAT Database Server | TS6420 (для TwinCAT 2), который представляет из себя сервис TwinCAT. Устанавливается и покупается он отдельно. Затем автоматически встраивается в TwinCAT и "прозрачно" предоставляет всякие удобные средства для работы с базами данных. Впрочем так делают любые другие сервисы TwinCAT, поэтому сам по себе он не сервер, а сервис — удобная прослойка, позволяющая автоматизировать ряд действий с базами данных.

Сервис Database Server | TF6420 для TwinCAT 3 умеет чуть больше. Во первых, он предоставляет объектно-ориентированную (ООП) библиотеку сущностей для доступа к БД, а во вторых, позволяет работать с базами не только из ПЛК-задачи, но и с помощью модулей C++.

Общение с невидимыми сервисами, начинается с конфигуратора: с помощью реактора запросов SQL Query Editor мы готовим запросы, а затем на выходе получаем XML-файл с конфигурацией (настройками) для БД. После этого не составит труда подключиться к базе данных из программы ПЛК-рантайма.

Работать можно тремя разными способами (различной степени сложности):
  • Авто-конфигурация (Configure Mode) — мы используем только конфигуратор, а переменные и данные затем курсируют между контроллером и БД автоматически.
  • ПЛК-эксперт (PLC Expert Mode) — из программы ПЛК используем специальные функциональные блоки, которые читают/записывают данные, но не требуют знания специальных команд БД (без SQL-команд).
  • SQL-эксперт (SQL Expert Mode) — ФБ работающие непосредственно с SQL командами БД.

Существуют несколько различных топологий сети для работы с БД. Не обязательно устанавливать TwinCAT Database Server на каждый ПЛК, можно собирать и компоновать данные различными способами и только затем отправлять в БД. Аналогично и с SQL-сервером — его можно установить как на каждый ПЛК, так и на выделенный сервер, отдельно и в единственном экземпляре.


Конфигуратор


Инструмент интегрируется с Microsoft Visual Studio. Конфигурация интегрируется в решение (solution) как отдельный проект по аналогии с TwinCAT Measurement (цифровой осциллограф). Унификация, интеграция и прочая синергия, проповедуемая отделом маркетинга.

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

Возможна ситуация когда несколько решений (solution) работают с одинаковыми базами (с одинаковыми настройками). Чтобы не плодить одинаковые настройки, в конфигуратор встроена поддержка коллекций настроек (пул настроек, Database Pool), из которых простым перетаскиванием можно быстро вытащить типовую настройку в новый проект.

SQL Query Editor — великолепный инструмент, позволяющий создать или отладить SQL-команды и сильно упростить работу с таблицами БД. Причем этот же инструмент позволяет работать не только с конфигуратором, но и впоследствии работать с записями базы данных в рабочих условиях. Также впоследствии не составит труда отредактировать структуру таблиц, типы полей, имена и другие свойства таблицы.

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


Авто-конфигурация


Другое название — группы автологирования (AutoLog Groups); они позволяют работать с БД без программирования. Выглядят как группы символов (переменных, symbols) закрепленные за таблицами и автоматически сохраняемые циклически или только при изменении данных.

Добавляется эта штука кликом правой клавишей мыши и выбором пункта меню Add AutoLogGroup. Все устроено действительно просто. Затем мы можем донастроить автологирование:
  • Выбрать тип старта — вручную или автоматически вместе с загрузочным проектом (boot project).
  • Задать время цикла опроса — как часто сбрасывать данные в базу.
  • Как записывать — добавлять, перезаписывать уже существующие (обновлять) или использовать кольцевой буфер по времени или количеству записей.
  • Режим записи — циклический, только при изменении значений.

Затем у нас появляется три ответвления для каждой из подготовленных групп:
  • AdsDevice — источник (и только источник) данных. Можно подключаться по именам символов или по индексу группы-смещение.
  • Symbols — открывает браузер целевой системы и отображает все символы ADS-устройства. По аналогии с осциллографом, где можно выбирать из каких переменных (а правильнее сказать символов) мы будем считывать данные.
  • DBTable — выбираем таблицу в которую будут записаны данные переменных. Нас предупредят, если мы попробуем выбрать неподходящую таблицу. Если же необходимо поступить как-то оригинально, то напротив полей таблицы можно выбрать переменные, подготовленные в пункте Symbols.


Режим автоконфигурации


FB_PLCDBAutoLog
  • RunOnce() — выполнить группу один раз. Например, по событию в контроллере.
  • Start()
  • Stop()
  • Status() — контроль ошибок и состояние обмена данными.


ПЛК-эксперт


Читать и записывать данные переменных вручную, но без применения SQL-команд.

FB_PLCDBWrite
  • Write()
  • WriteBySymbol()
  • WriteStruct()

FB_PLCDBRead
  • Read()


SQL-эксперт


Совсем низкоуровневый подход: отправляем SQL-команды, работаем с транзакциями, выполняем хранимые процедуры (stored procedures) и другие прямые действия с БД.

FB_SQLStoredProcedure
  • Execute()
  • ExecuteDataReturn()
  • Release()

FB_PLCDBCmd
  • Execute()
  • ExecuteDataReturn()

FB_SQLResult
  • Read()
  • Release()

FB_SQLCommand
  • Execute()
  • ExecuteDataReturn()


Поддерживаемые базы данных


В справочной системе есть список с нюансами:
  • Microsoft
    • MS SQL database
    • MS SQL Compact 
    • MS Azure SQL 
    • MS Access 
    • MS Excel 
  • ODBC
    • PostgreSQL
    • DB2
    • InterBase
    • Firebird
  • NET / ODBC — MySQL
  • OCI / ODBC — Oracle 
  • SQLite
  • ASCII-файл
  • XML базы данных

Можно подключить другие сервера БД. Для этого достаточно установить драйвер ODBC и настроить строку подключения.


Вопросы и ответы


Q: Что дальше?
A: TwinCAT Database Server все еще развивается. Активно разрабатывается интерфейс для C++ модулей реального времени. Работают над поддержкой модных NoSQL баз данных.

Q: Какая необходима версия TwinCAT XAE и Visual Studio?
A: XAE build 3.1.4012, VS 2013/2015. Не поддерживается VS 2010.

Q: Поддержка старых файлов конфигурации.
A: Да, поддерживаются. Старые ФБ также работают. Есть односторонняя преемственность.

Q: Влияет ли AutoLog на нагрузку контроллера и насколько?
A: Зависит от количества символов, контроллера и много еще чего. Попробуйте, но вообще это просто подключению к ADS-порту и обычное чтение данных через ADS.

Q: Контроль ошибок?
A: Да, есть. В том числе и через ФБ.

Q: Как дела с логированием оверсамплинг данных?
A: Поддерживаются не только стандартные типы SQL, но и работа с потоками байт (byte streaming). Уже есть рабочие проекты, но конечно же все будет зависеть от производительности и ресурсов конечных систем.

February 9, 2017

Указатель на ФБ

Указатели — это прямой путь в память контроллера. Путь — ничем не ограниченный и крайне опасный: одно не верное движение и вот вы уже в неразмеченной области памяти, отстреливаете себе ногу, вызывая исключение Page Fault! PLC is stopped. Что делать и как быть?

Начнем с простого:

PROGRAM MAIN
VAR
    A  : INT;
    B  : WORD;
    
    pA : POINTER TO INT;
    pB : POINTER TO WORD;
    dw : DWORD;
END_VAR

[...]

pA  := ADR(A);
pA^ := -1;


Переменная А в результате будет равна -1. При попытке сделать аналогичное с переменной B — pB^ := -1; получим ошибку компилятора еще на этапе сборки проекта. Объяснение простое — мы объявили pB как указатель на целый и всегда положительный тип WORD, а пытаемся пропихнуть число со знаком, типа INT. Компилятор бдит.


Выход за границу


Вообще, указателям можно присваивать все, что угодно, лишь бы справа был POINTER TO или DWORD (UDINT):

dw  := ADR(B);
pB  := dw;
pB^ := 123;

ADR возвращает число типа DWORD — это адрес переменной. Поэтому можно присвоить этот адрес указателю pB, а затем разыменовать указатель с помощью оператора ^ и присвоить новое значение для указанной переменной B. После всех действий B будет равен 123.

Оператор ^ применим только к указателям. Нельзя разыменовать переменную другого типа: dw^ := 123; — получим ошибку: '^' needs a pointer type. Поэтому хранить адрес можно в обычных переменных, но работать с адресами получится только через указатели.

Добавим перца — указателю можно присвоить любой адрес или любой другой указатель, указывающий на произвольный тип:

pA  := pB; (* pA теперь указывает на переменную 'B' типа WORD *)
pA^ := -2;

Результат: B = 65534 и ошибки здесь нет. Мы объявляли pA, как указатель на целое со знаком, то есть переменную типа INT, а переменная B — это целое беззнаковое, поэтому бит знака превратился в значимый разряд и дальше бла-бла-бла...

До сих пор у нас совпадал размер переменных — обе занимали ровно два байта. Что будет, если мы сделаем так:

A   : INT;
B   : BYTE;

[...]

pA  := pB; (* pA теперь указывает на переменную 'B' типа BYTE *)
pA^ := 1234;

B = 210;

...и опять без ошибки, но она может легко возникнуть, так как мы уже вышли за пределы переменной: 1234 занимает в памяти два байта, а мы записываем это число в переменную B типа BYTE, длиной один байт, как нам сообщает Капитан Очевидность. Таким образом легко совершить целый ряд безобразий: вылезти за пределы переменной или массива, вызвать сбой при обращении к странице памяти, остановить контроллер и технологический процесс. Завод встал, рабочие с факелами идут карать Франкенштейна.


Указатель на код


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

Для этого эксперимента нам понадобится:
  • Глобальная переменная, значение которой будет изменяться функциональными блоками.
  • Два ФБ, по разному изменяющие содержимое глобальной переменной.
  • Два указателя, которые мы будем испытывать, сталкивая лбами.
  • Немного терпения разработчика.

А давайте как физики в эксперименте про квантовую телепортацию — назовем наши функциональные блоки "Алиса" и "Боб" (в алфавитном порядке):

VAR_GLOBAL
    g_Result : STRING := 'Хзкт';
END_VAR

[...]

FUNCTION_BLOCK Alice
VAR_INPUT
    Name : STRING; (* пригодится позднее, когда появится злой двойник *)
END_VAR

g_Result := 'Алиса';

[...]

FUNCTION_BLOCK Bob
g_Result := 'Боб';

[...]

PROGRAM MAIN
VAR
    Al  : Alice;
    Bo  : Bob;
    pAl : POINTER TO Alice;
    pBo : POINTER TO Bob;
END_VAR

[...]

pAl := ADR(Al);    (* Указатель на ФБ Алиса *)
pBo := ADR(Bo);    (* Указатель на ФБ Боб *)

pAl^(); (* ФБ можно вызывать через разыменованный указатель *)


Каждый из ФБ записывает в глобальную переменную g_Result соответствующее имя: в зависимости от того, чей экземпляр мы вызовем через указатель, мы получим разные имена в глобальной переменной. На этот раз, получим результат 'Алиса'.

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

pAl := ADR(Bo);  (* Указатель на ФБ Боб *)
pAl^();


И если вы подумали, что получился 'Боб', то вы не правы — g_Result по прежнему равно 'Алиса'. Система следит за типом указателя, не давая сменить его на неправильный, но делает это молча — никому, ни о чем не сообщая.

Давайте создадим второй экземпляр ФБ Alice и переведем указатель на него, то есть присвоим указателю указатель, но теперь правильного типа POINTER TO Alice:

VAR
    Al     : Alice := (Name := 'Alice Original');
    AlTwin : Alice := (Name := 'Alice Evil Twin');

[...]

pAl := ADR(Al);       (* Указатель на ФБ Алиса *)
pAl := ADR(AlTwin);   (* Указатель на ФБ близнеца Алисы *)
pAl^();


На этот раз, указатель изменился:



TwinCAT 3


Скопируем проект, с небольшими отличиями в объявлении глобальной переменной:

VAR_GLOBAL
    Result : STRING := 'Хзкт';
END_VAR

[...]

FUNCTION_BLOCK Alice
G.Result := 'Алиса';  (* аналогично для Боба *)

[...]
    
pAl := ADR(Al);       (* Указатель на ФБ Алиса *)
pBo := ADR(Bo);       (* Указатель на ФБ Боб *)

pAl := pBo;

pAl^();


В результате получим — 'Боб', то есть G.Result = 'Боб'. В TwinCAT 3 указатели... э-э-э, гибкие? изменчивые? динамические? отзывчивые?


Нечеловеческий эксперимент над роботами


Жуткий по сложности и непонятности эксперимент над указателями в TwinCAT 2:
  1. Ссылаемся указателем pA на функциональный блок Alice.
  2. Получаем адрес функционального блока Bo.
  3. Получаем группу:смещение указателя pA.
  4. Через функции ADS записываем (подменяем) адрес указателя pA.
  5. Разыменовываем указатель pA и вызываем функциональный блок.

Зачем это нужно? Если раньше мы могли предположить, что компилятор как-то там отслеживает все наши махинации при сборке проекта: контролирует и корректирует указатели, то теперь мы те же действия будем делать уже во время работы контроллера. Компилятор о них ничего не узнает:

PROGRAM MAIN
VAR
    ReadSymInfo : PLC_ReadSymInfoByName;
    SymInfo     : SYMINFOSTRUCT;
    WriteAds    : ADSWRITE;

    Al          : Alice;
    pAl         : POINTER TO Alice;
    Bo          : Bob;
    AddrBob     : UDINT;

    state       : UINT;
END_VAR

[...]

CASE state OF
0:
    pAl     := ADR(Al); (* 1 *)
    AddrBob := ADR(Bo); (* 2 *)
    state   := 100;

100: (* 3 *)
    ReadSymInfo(
        NETID   := '',
        PORT    := 801,
        SYMNAME := 'MAIN.pAl',
        START   := TRUE,
        SYMINFO => SymInfo);

    IF NOT ReadSymInfoByName.BUSY THEN
        ReadSymInfoByName(START := FALSE);
        state := 200;
    END_IF

200: (* 4 *)
    WriteAds(
        NETID   := '',
        PORT    := 801,
        IDXGRP  := SymInfo.idxGroup,
        IDXOFFS := SymInfo.idxOffset,
        LEN     := 4,
        SRCADDR := ADR(AddrBob),
        WRITE   := TRUE);

    IF NOT WriteAds.BUSY THEN
        WriteAds(WRITE := FALSE);
        state := 300;
    END_IF

300:
    pAl^(); (* 5 *)

END_CASE

В результате все равно получится 'Алиса' и это несмотря на то, что указатель, судя по адресу, указывает на 'Боба':



Предположения


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

В TwinCAT 3 завезли объектно-ориентированное программирование с классами, методами и, самое главное, интерфейсами. Последние как раз нуждаются в полиморфизме и гибких указателях (vtable). Поэтому с указателями в TwinCAT 3 все ожидаемо-хорошо.

И там, и там можно устроить крах системы, выйдя за пределы выделенной памяти. Осторожнее там с указателями!

February 2, 2017

События под максимальной нагрузкой

Насколько сильно нагружают процессор ПЛК асинхронные функции TwinCAT.ADS API? Стоит ли пользоваться подпиской на переменные ПЛК-задачи и сидеть в ожидании уведомлений об изменившихся данных или сразу написать polling-велосипед? Я уже измерял скорость передачи данных через ADS.API под .NET, теперь посмотрим как нагружают систему самые тяжелый асинхронные функции событийной модели.

TcAdsClient client = new TcAdsClient();
client.Connect("5.24.134.118.1.1", 801);


Имя для области памяти


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

PROGRAM MAIN
VAR
    a : WORD;
END_VAR

[...]

a := a + 1;
a := a AND 16#00FF; (* А *)

Наращиваем переменную MAIN.а типа WORD каждый цикл на единицу, а старшую часть обнуляем (* А *) чтобы она оставалась неизменной. Для начала подпишемся на всю переменную целиком и убедимся, что система работает.


Адрес переменной


На переменную можно подписаться через ее имя, а можно через адрес в памяти (индекс группы + смещение). И прежде, чем начинать подписываться, я хочу выяснить как мне узнать адрес переменной или хотя бы, где вообще лежат переменные ПЛК-задачи. Для этого воспользуемся функцией ReadSymbolInfo:

ITcAdsSymbol info = client.ReadSymbolInfo("MAIN.a");
Console.WriteLine($"Group:Offset = {info.IndexGroup}:{info.IndexOffset}");

Как результат получим адрес Group : Offset = 16448 : 0. В моем тестовом примере переменная в программе одна единственная, поэтому она расположена в начале области памяти, и поэтому ее смещение равно нулю.
В статье справочной системы "Index-Group/Offset" Specification of the PLC services можно найти упоминание, что память ПЛК-задачи начинается с адреса 0x00004040 = 16448.
Теперь, обращаясь через байтовые смещения, мы можем адресовать только часть переменной. Заменим имя этой переменной на адрес области памяти — Group: 0x4040, Offset: 0, длина 16 разрядов (тип ushort или WORD):

client.AdsNotificationEx += Client_AdsNotificationEx;
client.AddDeviceNotificationEx(0x4040, 0, AdsTransMode.OnChange, 0, 0, null, typeof(ushort));

[...]

private void Client_AdsNotificationEx(object sender, AdsNotificationExEventArgs e)
{
    Log(e.Value);
}


Система работает — как и было задумано: уведомления приходят в метод Client_AdsNotificationEx, значение переменной доступно через поле e.Value.


Часть вторая


Подпишемся теперь на младший байт переменной MAIN.a, то есть на ту половину переменной, которая увеличивается каждый цикл. Для этого мы изменим тип данных с ushort (16-разрядов — эквивалент WORD) на byte (8 разрядов):

client.AddDeviceNotificationEx(0x4040, 0, AdsTransMode.OnChange, 0, 0, null, typeof(byte));

Уведомления по прежнему приходят.

Теперь подпишемся на старшую половину переменной. Для этого достаточно увеличить смещение (Offset) на единицу, то есть перейти на один байт дальше:

client.AddDeviceNotificationEx(0x4040, 1, AdsTransMode.OnChange, 0, 0, null, typeof(byte));

Старший байт переменной искусственно обнуляется каждый цикл (* А *), поэтому старшая часть переменной остается неизменной. И всё — сообщения больше не приходят, кроме самого первого после подписки.
Так было задумано разработчиками — всегда приходит первое сообщение. До подписки мы не знали значение переменной, поэтому для нас оно было неопределенным. После подписки неизвестное значение сразу же может стать известным, поэтому система отправляет нам первое (и возможно единственное) сообщение о текущем значении переменной.

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


Подписываемся и замеряем


В качестве испытуемого возьмем CX9020. Начнем проверку с TwinCAT 2, а чуть позже сравним с третьей версией. Сейчас же идея такая: подписываемся на пару сотен переменных, а затем смотрим через System Manager нагрузку на процессор. Пишем код:

PROGRAM MAIN
VAR
    Arrgh : ARRAY [1..10000] OF WORD;
    i     : INT;
END_VAR

[...]

FOR i := 1 TO 1000 DO
    Arrgh[i] := Arrgh[i] + 1;
END_FOR


Подписываемся:

for (int i = 0; i < 200; i++)
    client.AddDeviceNotificationEx(0x4040, i * 2, AdsTransMode.OnChange, 0, 0, null, typeof(ushort));


Замеряем:


Очень много для программы, которая практически ничего не делает. Давайте попробуем приблизиться к заявленному пределу в 550 переменных (Асинхронные функции TwinCAT.Ads API):


Здесь 500 переменных. Результат ужасный.


Полл Шред


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

[...]

Прошел час. Пропустим несколько итераций эксперимента и сразу рассмотрим опрос 10 000 переменных. Для этого нам просто необходим отдельный поток:

CancellationTokenSource cancelTokenSource;

private void StartButton_Click(object sender, EventArgs e)
{
    cancelTokenSource = new CancellationTokenSource();
    SynchronizationContext uiContext = SynchronizationContext.Current;
    Thread pollThread = new Thread(() => PollThread(cancelTokenSource.Token, uiContext));
    pollThread.Start();

[...]

private void PollThread(CancellationToken token, object uiContext)
{
    using (var client = new TcAdsClient())
    {
        client.Connect("5.24.134.118.1.1", 801);

        while (!token.IsCancellationRequested)
        {
            for (int i = 0; i < 10000; i++)
            {
                if (token.IsCancellationRequested)
                    goto EndOfThread;

                ushort a = (ushort)client.ReadAny(0x4040, i * 2, typeof(ushort));
            }

            Thread.Sleep(0);
        }

        EndOfThread:;
    }
}


Старт. Результат:



Это был регулярный опрос 10 000 переменных за раз. Если понравилось — давайте попробуем регулярно считывать целиком весь массив. Для этого заменим цикл for на чтение массива целиком:

ushort[] buf = new ushort[10000];
buf = (ushort[])client.ReadAny(0x4040, 0, buf.GetType(), new int[] { buf.Length });


Результат аналогичный — нагрузка не превышает 20%.


Грузите апельсины бочками


Асинхронные функции сильно нагружают систему контроллера. Для старших моделей ПЛК слово "сильно" можно заменить на "значительно", но легче от этого не станет. Тем не менее, если нужно следить за десятком переменных, то можно обойтись асинхронными функциями. Если же переменных много, то есть два выхода: сделать опрос вручную или объединить переменные в структуру/массив.

Рассмотрим второй вариант. Я изменил код и подписался сразу на целый массив из 10 000 ячеек типа WORD. Теперь, чтобы пришло сообщение, достаточно изменить значение только одной ячейки массива (или одного поля структуры, если мы объединили переменные в структуру):

ushort[] buf = new ushort[10000];
client.AddDeviceNotificationEx("Main.Arrgh", AdsTransMode.OnChange, 0, 0, null, buf.GetType(), new int[] { buf.Length});


Результат:



Вывод: перед отправкой упаковывайте данные в структуры и отслеживайте их как единое целое.


TwinCAT 3.1


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

Подписываемся на 1000 переменных (контроллер тот же — CX9020):



Но(!) по прежнему нельзя подписаться на 10 000 переменных — большое количество подписок приведет к ошибке: "Ads-Error 0x751 : A hashtable overflow has occured". Это внутренняя ошибка переполнения словаря переменных в библиотеке TwinCAT.Ads, то есть по прежнему имеем ограничение на количество переменных. Мелочь, а неприятно.


Замыкаем


Пора найти плюсы и минусы и замкнуться в сферах применения:

Подписка на события (событийная модель):
+ Удобство использования: методы обработки данных вызываются только тогда, когда переменная изменилась.
+ Регулярность сообщений не зависит от программы на ПК.
+ Снижение нагрузки на сеть: обменом данными занимаются AMS-роутеры, которые отправляют только измененные данные.
– Ограничение на частоту уведомлений (не быстрее 1 миллисекунды).
– Ограниченное количество переменных: 550 штук — TwinCAT 2, несколько тысяч в TwinCAT 3.
– Чем больше переменных, тем выше нагрузка на процессор контроллера.

Регулярный опрос (поллинг):
+ Неограниченное количество переменных.
+ Низкая нагрузка на процессор ПЛК.
+ Максимальная скорость обмена данными (до 50 мкс, быстрее TwinCAT просто не умеет).
– Требуется вручную обмениваться и обрабатывать значения переменных.
– Дополнительная нагрузка на сеть. Запросы и ответы ходят по сети регулярно, независимо от того — изменились данные или нет.
– Дополнительная нагрузка на программу ПК и сложность в синхронизации данных в многопоточном приложении.

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

И в любом случае TwinCAT 3 работает быстрее и легче.

January 31, 2017

Вебинар. Технологии измерений на базе аналоговых модулей

24 января этого года, Мартин Подрушек (Martin Podrouschek) рассказал про аналоговые модули Бекхофф, а затем обещал в следующем вебинаре рассказать про то, как их конфигурировать и наблюдать на цифровом осциллографе в их естественной среде обитания.

Когда-то давно была подборка физических интерфейсов и модулей расширения, а сейчас мы узнаем некие подробности об аналоговых модулях (как входных, так и выходных). Также можно ознакомиться с флаером Extremely accurate, fast and robust: High-end measurement technology from Beckhoff, где есть более-менее подробное изложение вебинара (правда, на английском языке).


Программная часть


Программная часть, отвечающая за обработку и настройку аналоговых модулей, условно делится на несколько частей:
  • Scope View|Server — цифровой осциллограф клиент/сервер, бесплатный в базовой поставке.
  • Bode Plot — ЛАФЧХ, диаграммы Боде.
  • Analytics — более комплексный анализ данных в совокупности и за большой промежуток времени, со сбором данных в "облаке" и т. п.
  • Configuration — настройка модулей.
  • Filter Designer — настройка цифровых фильтров Баттерворта, Чебышева, НЧ, ВЧ, и т. п. Можно настраивать параметры и рисовать кривые фильтров, затем загружать их в модули.

Ниже и ближе к железу, лежит слой обеспечения реального времени, про который мы должны помнить, что он работает с заданной цикличностью и ни микросекундой больше или меньше. Это важно помнить и знать, особенно, относительно измерительной аналоговой техники, где придется много раз делать преобразования из аналога в цифру и обратно.
Аналоговые величины существуют как бы в двух измерениях — это дискретизация по времени (TwinCAT обеспечивает синхронизацию между устройствами с точностью до ±100нс) и значение измеряемой величины. Важно помнить об этом, так как точность измерения определяется по двум координатам.
Еще ниже (но не всегда) есть дополнительные библиотеки-драйверы, которые обеспечивают работоспособность специализированных модулей. Например, библиотеки Energy Library для модулей измерения параметров электроэнергии (Energy Measurement) — модули серии EL3xxx, KL3xxx или библиотека CM library контроля состояния (Condition Monitoring) — IEPE акселерометры и другие датчики.


Пошаговый выбор модулей


Сейчас в каталоге доступно порядка 100 модулей. Чтобы не потеряться среди них, предлагается пошаговый подход к выбору.

После определения какой функционал требуется от модуля, можно выбрать тип защиты корпуса:
  • IP20 — для установки в шкафах автоматизации. Серии BK, BC, EL, KL, EM, KM, ES, JS, IPC|CX|CP.
  • IP67 — для установки под открытым небом, дождем и снегом. Серии: EP, EPP, EQ, ER, IP.

Дальше интересней, так как Beckhoff по сути не настаивает на обязательном использовании EtherCAT для подключения модулей, а предлагает на выбор различные полевые шины: Profibus, Profinet, Sercos II, много еще чего, ну и EtherCAT конечно же. Для всего этого зоопарка шин существуют соответствующие коплеры (coupler), объединяющие модули в удаленные или локальные группы.

Дальше необходимо определиться с аккуратностью или погрешностью измерения (±%) и пропускной способностью (100Гц..100кГц).

Также важным параметром являются тайминги и частота дискретизации:
  • XFC — модули умеют делать оверсамплинг, что повышает точность, за счет интерполяции дополнительных отсчетов между тиками системы реального времени. Только для шины EtherCAT.
  • DC — поддержка синхронизации распределенных часов.
  • Timestamp — фиксируется ли отпечаток времени события (важно, так как измерение может произойти в произвольный момент длительности цикла).

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


Быстродействие и точность


Не стоит считать высокоточными — модули с высокой разрядностью оцифровки величины (hi resolution). 16 или 24 бит вовсе не означает, что эти модули точнее. Просто не имеет смысл проводить точные измерения, а затем терять эту точность из-за малой разрядности оцифровки. Выбирая модули, учитывайте обе величины — и разрядность, и погрешность измерения.

Во втором квартале 2017 года должна выйти новая серия модулей ELM в железном корпусе и для сверхточных измерений:
  • 24 разряда;
  • до 50 000 отсчетов в секунду;
  • синхронизация с точностью до ±100нс;
  • интегрированная диагностика подключений и работы функционала.

Серия модулей для шины K-bus пока еще существует — это модули KL3xxx. Следует учитывать, что из-за ограничений шины K-bus, эти модули не могут обеспечить высокое быстродействие.

January 26, 2017

Задание положения на лету

Можно ли сбросить значение текущего положения оси "на лету", во время ее движения? Не вбросить новое задание на выполнение (это совершенно другая задача "контурного режима"), а просто сказать, что в данный момент, вот это место — это позиция 3 200 851 или 0, или -600, или любое другое число.

Можно, хотя всё говорит об обратном. System Manager и XAE отказываются, сообщая: "Axis function is not allowed in motion!", — это означает, что функция недоступна во время движения.



Справочная система говорит, что для задания позиции "на лету" предназначен относительный режим (Relative), но ничего не говорит об абсолютном (Absolute) режиме. А ведь, на самом деле, всё работает, правда при условии, что вы делаете это из ПЛК-задачи и с помощью функционального блока MC_SetPosition.
Если есть вероятность что ваша программа будет работать на более ранних версиях TwinCAT, то лучше довериться System Manager и считать, что нельзя задавать позицию "на лету". Для большей уверенности стоит проверить, так как я проверял только на версиях 2.11.2254 и 3.1.4020.

Итак, сбросить позицию "на лету" в произвольное значение во время движения — можно. Рассогласование при этом не возникнет (хотя справочная неуверенно заявляет о возможности обратного). Что еще нужно знать о функции MC_SetPosition?

Существует два режима, передаваемые через параметр Mode. Абсолютный режим (Mode = FALSE) просто задает новое текущее значение позиции. Относительный же (Mode = TRUE) — задает смещение от текущего значения позиции. Например, если в относительном режиме задать отрицательное число — позиция уменьшится на заданное число, если положительное — увеличится.

В будущих версиях библиотеки (относительно версии 3.02.52 от 19.10.15) существует вероятность замены имени параметра Mode на Relative. Пока же это название остается прежним из соображения совместимости.

Когда-то давно, теперь уже в далеком 2005 году, функция MC_SetPosition называлась MC_SetActualPositionOnTheFly и ответ был сразу же понятен из ее названия.

January 24, 2017

Питание энкодерной оси

Нужно ли активировать питание энкодерной оси (Encoder Axis) с помощью функционального блока MC_Power?


Нет, не нужно. Достаточно просто регулярно читать статус переменной, связанной с осью движения. Следующего достаточно:

PROGRAM MAIN
VAR
    EncoderAxis : AXIS_REF;
END_VAR

(* Обновляем информацию об оси движения *)
EncoderAxis.ReadStatus;

(* Работаем с текущей позицией *)
IF EncoderAxis.NcToPlc.ActPos > 0 THEN
[...]
Стоит учитывать, что без вызова метода ReadStatus, автоматически обновляются только статусные поля структуры NcToPlc (поля, отвечающие за состояние оси). Поля, содержащие позицию, скорость и другое, обновляться без вызова ReadStatus не будут. Это также относится и к обычным (Continuous Axis) приводным осям.
Делаем предварительный вывод ― энкодерная ось отличается от обычной приводной (Continuous Axis) только тем, что преобразует параметры движения в цифру, но не осуществляет управление. Следовательно, силовое питание энкодерной оси не требуется.

― Хорошо, а что будет если все-таки задействовать MC_Power? Сможет ли он запретить вращение в обратную сторону или что-нибудь подобное?
― Нет, не сможет. Энкодерная ось всегда находится в неподвижном состоянии. Поля State.HasBeenStopped и .StandStill всегда равны TRUE. State.MotionState также всегда равно MC_AXISSTATE_STANDSTILL. Теперь мы знаем, что MC_Power активирует и контролирует только управление движением (то есть выход), но никак не влияет на входные данные.

Что еще полезного в структуре State?

Поля .Moving / .NotMoving ― показывают, движется ли ось. Причем, ожидаемо, каламбур: .NotMoving = NOT .Moving. Направление движения помогают определить поля .NegativeDirection и .PositiveDirection.

Не ждите полезной информации от полей Error. Лучше следите за Status.IoDataInvalid. Это поле показывает валидность данных. Поле становится равным TRUE, когда данные неверны, что можно интерпретировать как ошибку энкодерной оси.

January 13, 2017

System Manager и четыре рантайма

При попытке создать несколько рантаймов и добавить несколько ПЛК-проектов, System Manager начинает капризничать, брыкаться, а в конце выдает ошибку: Runtime system is used by another project!



... и это не баг и не фича — это нюанс, который придется обойти.

Проект строится с помощью двух системных программ: System Manager — конфигуратор ПЛК и PLC Control в котором непосредственно разрабатывается программа контроллера. В какой-то момент разработки необходимо пробросить между ними мостик и познакомить их, передавать настройки из одной в другую и обратно. Для этого служат файлы с расширением .tpy, которые делает PLC Control при сборке (Project → Build) или пересборке (Project → Rebuild all) проекта.
Кроме файлов TPY, также существуют файлы TPA. Их создает System Manager, когда вы добавляете в конфигурацию новый проект или пересканируете уже существующие приекты (Rescan Project...). В эти TPA-файлы System Manager сохраняет конфигурацию переменных ввода/вывода (%I*, %Q*), заменяя звездочки (*) на реальные адреса.

При последующей пересборке проекта, PLC Control увидит файл TPA, созданный ранее System Manager'ом, и добавит в ПЛК-проект конфигурацию переменных, которую впоследствии можно увидеть в Resources → Global Variables → TwinCAT_Configuration.
В файле TPY находится полная информация о проекте, в том числе информация о рантайме, в котором планируется исполнять ПЛК-программу. К сожалению, невозможно выбрать нужный рантайм вручную, если вы разрабатываете проект без контроллера на столе и рядом с вами. По умолчанию система автоматически подставляет в проект первый рантайм, а затем сама же на это ругается, когда вы пытаетесь добавить второй, третий или четвертый проект в System Manager: ведь первый рантайм уже занят и нужно выбрать другой?

Чтобы покинуть тупиковую ситуацию, придется вручную отредактировать файл TPY, а конкретнее поле <Port> настроек роутинга <RoutingInfo> проекта <PlcProjectInfo>. Откройте в любом текстовом редакторе файл с расширением .tpy:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--This file is created by the TCatPlcCtrl automatically. Manually changes will be overwritten!-->
<PlcProjectInfo xmlns:p="http://www.beckhoff.com/2002/01/TcPlcProjectDesc">
    <ProjectInfo>
[...]
    </ProjectInfo>
    <RoutingInfo>
        <AdsInfo>
            <NetId>0.0.0.0.0.0</NetId>
            <Port>801</Port>
            <TargetName>
                <![CDATA[Target: Local (192.168.56.1.1.1), Run Time: 1]]>
            </TargetName>
        </AdsInfo>
    </RoutingInfo>

Номера портов жестко фиксированы: 1 — 801; 2 — 811; 3 — 821; 4 — 831. Достаточно заменить номер порта на соответствующий необходимому рантайму, а все остальные упоминания о рантайме можно оставить без изменений: на них система не обращает никакого внимание. Сохраните файл и без проблем добавьте его в конфигурацию System Manager'а.