February 18, 2018

История с крышечками

Нельзя просто так взять, и бросить контакты шины неприкрытыми. Для этого существует Bus end cap или Bus end cover — защитная крышка. По сути, это просто кусок промышленной пластмассы, защищающий контакты шины последнего модуля, но есть нюансы.

Во времена перехода от шины K-bus к шине EtherCAT (E-bus), выпускался широкий 12 миллиметровый модуль (Bus End Terminal), похожий на обычный модуль, только без электроники на борту. Заодно обратите внимание на слово Terminal:


Был он толстый и занимал дополнительную позицию на дин-рейке. Это всем мешало, и тогда было принято решение перейти на оптимальные по ширине крышки:


Соответственно, слева-направо: EL9011, ELM9012 и ELX9012, а в конце пунктирный образец для выпиливания лобзиком и, по совместительству, схема электрическая принципиальная. Несмотря на отсутствие электроники на борту, System Manager как-то эти модули обнаруживает и добавляет в конфигурацию. Черная крышка для измерительных модулей ELM, синяя — для электропожаровзрывобезопасных модулей ELX, серая — для обычных модулей EtherCAT. Желтая крышечка TwinSAFE пока что не существует или еще не прошла сертификацию.

Внезапно, в списке продукции всплывает EL9012, которая выглядит точь-в-точь как EL9011, но если первая называется английским словом cover (укрытие), то вторая всего-лишь cap (крышечка). Какую ставить, и есть ли разница? Давайте сравним...

12-я толще: 8 мм против 5 мм одиннадцатой. Зато 11-я лучше сертифицирована: три сертификата CE, UL, Ex, а у 12-й только один CE. Так как 12-я в три раза дороже 11-й, делаем вывод, что разница в стоимости зависит только от количества пластика. Ну и последнее, 12-я закрывает весь бок терминала целиком и прячет под защиту и шину, и контакты подпитки модулей (cover for power and E-bus contacts), а EL9011 закрывает только контакты шины (cover for the E-bus contacts). Вот и вся разница. Достаточно разместить фото крышек сбоку и разница была бы понятна сразу.

По остальным защитным, разделительным и прочим крышкам можно прочитать в EL9xxx.

February 15, 2018

Categories of TwinCAT Libraries

When you are ready to publish your library, it is a good idea to place it under the one or several categories in the library repository. Later, it helps you and other developers to find your library among the others at the local repository.

But there are no any official category lists – at least, I have not found any others one.

We can extract one or two (maybe more) categories from the one precisely selected library from the local storage at
x:\TwinCAT\3.1\Components\Plc\Managed Libraries. But we should choose carefuly because that source library should contain required categories. Or we can load category list from the "Description File" with *.libcat.xml extension. But (once more) I can’t find this description file – anywhere on local system or in the internet.

Then, I have made this category list myself.



I uploaded this unofficial list on GitHub. You can download latest version from beckhoff-tc31.libcat.xml or you can clone the whole project Tc_LibcatXml to your laptop or to your github repository or to any other place and finally make your library more shiny.

Категории библиотек TwinCAT 3

Предположим, вы разработали библиотеку или портировали чужую, типа библиотеки OSCAT под TwinCAT 3. Наступает ответственный момент оформления библиотеки, вы заполняете графы описания и решаете выбрать категорию библиотеки (Library Categories), чтобы в дальнейшем быстро находить библиотеку в локальном репозитории,

А списка категорий нет.

Можно экстрагировать категории из других библиотек: они лежат в x:\TwinCAT\3.1\Components\Plc\Managed Libraries, а можно загрузить из некоего файла (From Description File...) с расширением *.libcat.xml, но файла также нет. По крайней мере, для оборудования и библиотек Бекхофф, я их так и не нашел.

Поэтому выдавил из библиотек свой собственный и неофициальный список.



Список можно найти на гитхабе, он лежит в открытом виде и готов к использованию — Tc_LibcatXml. По возможности я буду поддерживать его в актуальном состоянии, либо должен найтись способ получать эти категории из официального источника. Сейчас же доступны три не официальные версии — какое-то наследие TwinCAT 2, затем устаревший TwinCAT 3 и актуальная на данный момент версия beckhoff-tc31.libcat.xml.

February 13, 2018

ExST: расширенный ST

Язык ST (Structured Text) получил много полезных расширений, что в последствии назвали Extended ST. И это не только модное ООП. Я свалил все в кучу: и расширения, и просто малоизвестные вещи, к тому же, всё это подходит не только для TwinCAT, но и для CoDeSys-мира вообще. Начнем с мальчика для битья — оператора GOTO...

Язык ST все еще поддерживает оператор GOTO. Не прямо так гоу-ту, а как в ассемблере — JMP куда-то там на метку в коде. Выглядит это так:

label_infinitum:
(* полезный код *)
JMP myLabel_4321;

(* бесполезный кот *)

myLabel_4321:    // это метка для перехода
(* еще полезный код *)
JMP label_infinitum;

Избегайте бесконечно-вечных циклов.


CONTINUE


CONTINUE относится к расширению языка, а EXIT нет. Если вы не знали, то EXIT немедленно прекращает выполнение текущего цикла и выходит из него (текущая итерации FOR, WHILE или REPEAT не будет выполнена до конца). CONTINUE же прекращает текущую итерацию и немедленно переходит к началу цикла, выполнять следующую итерацию:

FOR i := 0 TO 100 BY 2 DO
    IF i MOD 3 = 0 THEN
        CONTINUE; // пропускаем инкремент переменной a и переходим к следующей итерации
    END_IF
    a := a + 1;
END_FOR

Осторожнее с вложенными циклами: EXIT выходит только из одного вложения. Для выхода из нескольких вложенных циклов, помогает команда JMP.

FOR j := 0 TO 100 DO
    FOR i := 0 TO 100 DO
        IF ??? THEN
            EXIT; // завершит цикл FOR i ...
        ELSE
            JMP finita_incantata;
        END_IF
    END_FOR
    // EXIT приведет сюда
END_FOR

finita_incantata:

Оператор MOD находит остаток от деления, как оператор % в C++.


Оператор установки S=


Запись A S= X; аналогична:

IF X THEN
    A := TRUE;
END_IF

Как только A станет равным TRUE, то никакое значение X уже не изменит A. Оператор S= никогда не сможет сбросить A в FALSE.

A        X       A
FALSE S= FALSE → FALSE
FALSE S= TRUE  → TRUE
TRUE  S= FALSE → TRUE
TRUE  S= TRUE  → TRUE


Оператор сброса R=


Запись F R= X; аналогична:

IF X THEN
    F := FALSE;
END_IF

Как только F станет равным FALSE, то никакое значение X уже не изменит F. Оператор R= никогда не сможет установить F в TRUE.

F        X       F
FALSE R= FALSE → FALSE
FALSE R= TRUE  → FALSE
TRUE  R= FALSE → TRUE
TRUE  R= TRUE  → FALSE


Присваивание


Присваивание как выражение (assignment as expression) можно делать так:

IF bVar := (i = 2) THEN
    i := i + 1;
END_IF

bVar — булева переменная, получает значение от выражения (i = 2), то есть будет равна TRUE когда (i = 2). Затем bVar оценивается в IF. Лучше не мудрить и не пихать в одну строку несколько команд сразу. Пишите так, чтобы было проще читать впоследствии. Компилятору все-равно как  вы написали, работать это будет одинаково, а вероятность ошибиться намного выше в сложносочиненной записи:

bVar := (i = 2);

IF bVar THEN
    i := i + 1;
END_IF


Можно присваивать нескольких переменных одновременно:

a:= b:= c:= k + 12;

Но не дайте обмануть себя видом и формой записи — здесь нет присваивания, передающегося справа-налево по цепочке! Аналогичная, но более длинная запись, выглядит так:

a := k + 12;
b := k + 12;
c := k + 12;

Выражение (k + 12) будет вычисляться три раза, поэтому выгоднее записать:

c := k + 12;
a:= b:= c;

Теперь вспомним операторы установки/сброса. Как будет работать следующая строка? Ответ в конце поста.

A S= F R= X;


Типизированные литералы


Звучит жутко умно, но это всего-лишь способ задать точный тип числа.

A := 123; — не говорит нам какого типа число 123 (литерал). Это точно не BOOL и скорее всего не REAL / LREAL (нет десятичной запятой). Возможны SINT, USINT, BYTE, INT, UINT, WORD, DINT, UDINT, DWORD. Так кто же из них?

Обычно тип литерала будет зависеть от типа переменной A. Поэтому можно просто уточнить запись литерала: A := 123.0; и тогда мы получим REAL. Но что если хочется BYTE, а переменная типа UINT?

Для этого тип можно обозначить точно: A := BYTE#127;

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


CASE ... ELSE


Если конкретный шаг CASE для какого-то значения переменной не существует, то CASE игнорируется целиком, за исключением если... Блок ELSE в CASE позволяет задать действие по умолчанию, если номер шага в CASE не задан:

CASE state OF
0:
    DoSomething();
100:
    DoSomething2();
...
2000:
    DoSomethingAgainNAgain();
ELSE
    DontKnowWhatHappenedLastTime();
END_CASE

Если вы знакомы с C++, то это аналогично: switch case ... default.


OR_ELSE


Работает аналогично OR, но если левая часть OR_ELSE уже равна TRUE, то правая часть не проверяется и не выполняется:

VAR
    a, z, b : BOOL;
    i : INT;
END_VAR

z := a OR_ELSE (b := (i = 2));

Если А равно TRUE, то правая часть OR_ELSE не будет выполняться и B никогда не сможет стать TRUE. Можно оптимизировать производительность, исключив лишний вызов функций. И теперь мы знаем, что для "обычного" OR, независимо от значения операндов, вызываются и проверяются обе части: и левая, и правая.


AND_THEN


Как AND, но правая часть AND_THEN обрабатывается только при условии, что левая часть  равна TRUE. Например, это удобно для проверок указателей:

IF (ptr <> 0 AND_THEN ptr ^= 99) THEN ...

Когда указатель не равен нулю и_тогда выполнить сравнение адреса указателя. Это позволяет избежать проблем с нулевым указателем. В обычном AND всегда обрабатываются обе части независимо от их значения.


Ответ


A S= F R= X;

// работает как

A S= X;
F R= X;

February 10, 2018

SLC USB 3.0 флешки

Бекхофф переходит на очень надежные SLC USB-флешки для переноса и хранения полезных данных. На чем были построены предыдущие не известно, но вариантов немного: или MLC — как в SSD, или TLC — как в бытовых флешках. По аналогии с 200x рублевыми купюрами, новые флешки выглядят так:

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

Еще раз — SLC (single-level cells) в теории надежнее, чем MLC и TLC. Там что-то порядка 100 000 перезаписей, против 3 000 и 1 000, соответственно. А еще SLC быстрее всех.

Флешки будут с интерфейсом USB 3.0 и объемом 4, 8, 16, 32 Гб и полной совместимостью с USB 2.0 и USB 1.1. Для желающих, есть разновидность с предустановленными спасительными BST (Beckhoff Service Tool): такие же объемы, но интерфейсы только USB 3 и 2. USB 1.1 для BST не поддерживается.

А раньше были такие:

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

Или даже такие, возможно, раздаточно-презентационные:


February 7, 2018

Деление на ноль

Давно ли вы делили на ноль?



Случайные ошибки


PROGRAM MAIN
VAR
    a, b, c : INT;
END_VAR

a := 12 / 0;

Такую случайную ошибку среда разработки отловит еще на этапе компиляции:



... но стоит только подставить переменную, как деление на ноль произойдет во время работы. И...

PROGRAM MAIN
VAR
    a, b, c : INT;
END_VAR

a := 12 / b; // <<<< !!!!

...TwinCAT переходит с состояние "стоп" (Stop), выбрасывая в консоль сообщение об ошибке:
Error 01.02.2018 15:02:24 456 ms | 'Port_851' (851): Exception (Exception Code: 0xc0000094, Integer divide by zero) in PLC Application Untitled1 Instance, Task PlcTask (EBP: 0xa9b5eeb8, EIP: 0xa7927056, ESP: 0xa9b5ee90)

Что произойдет если у вас несколько задач (тасков)? Остановится ли только "эта" задача или все сразу? Как поведет себя система в многоядерной архитектуре? И что с изолированными (isolated) выделенными ядрами?


Изолируем и форкаем


Добавляю и запускаю вторую, параллельную задачу. И сразу же роняю первую, установив переменную b := 0. Первая задача падает в точку останова, но вторая задача продолжает работать — счетчик тикает:



Пробую восстановить задачу после падения, устранив ошибку: значение переменной b := 1, затем пытаюсь продолжить выполнение первой задачи. Не получается. Пробую подтвердить на экране ПЛК сообщение об ошибке деления на ноль (кнопка ОК). TwinCAT целиком переключается в режим конфигурирования (синий значок), но теперь уже с остановкой всех задач: и ошибочных, и корректных. При этом:
  • Reset Cold — не помогает.
  • Rest Origin — помогает, так как он начисто сносит весь рантайм задачи (это заметно по предложению пересоздать порт при последующем запуске). Затем можно перезапустить задачу заново, но непонятно, что в это время творится с другими задачами.

При всех этих телодвижениях среда разработки VS 2015 ведет себя очень нестабильно, а может быть это контроллер ведет себя некорректно. Не уверен — кто из них. Возможно оба. В надежде повысить стабильность, я выделил одно из ядер ПЛК целиком под TwinCAT: 100% производительности в печенку процессора. Ничего не меняется. Пытаюсь перезапустить ошибочную задачу, но TwinCAT переходит в режим конфигурации, с полной остановкой всех ядер и задач. Версия TwinCAT контроллера: 3.1.4020.32.

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


Отлаживаем


Для отладки и только отладки (почему, я расскажу позже) TwinCAT предлагает нам стандартное средство из проверочных функциональных блоков — Object POUs for implicit checks. Теперь их можно создавать тыкая мышкой в: POUs → Add → POU for implicit checks... → +Division Checks. После этого в ПЛК-проект добавятся несколько автоматически сгенерированных функций. Необходимо перекомпилировать проект и перезагрузить его в контроллер целиком (download). Загрузка проекта на лету (online change) с сохранением данных (значений переменных) на этот раз недоступна, так как в проекте появились новые программные объекты (POUs).

Я экспериментирую с делением целого типа, вот пример такой функции:

// Implicitly generated code : DO NOT EDIT
FUNCTION CheckDivDInt : DINT
VAR_INPUT
    divisor:DINT;
END_VAR

// Implicitly generated code : Only an Implementation suggestion
{noflow}
IF divisor = 0 THEN
    CheckDivDInt:=1;
ELSE
    CheckDivDInt:=divisor;
END_IF;
{flow}

Несмотря на предостережение: "НЕ РЕДАКТИРОВАТЬ", — редактировать можно и нужно. Например, добавить сообщение об ошибке в лог и вытащить номер текущего шага из других программных объектов.

Интересно, что эта функция будет вызываться для каждой операции деления! Значением входного параметра divisor будет значение делителя/знаменателя из операции деления, а результат функции (возвращаемое значение) будет подставляться вместо делителя в операции деления. Попробуйте в теле функции заменить CheckDivDInt:=divisor; на CheckDivDInt:=2; и все ваши операции деления, независимо от значения делителя, превратятся в банальным делитель на двойку. Но стоит только поделить на ноль, как вместо делителя (равного нулю) будет подставлена единица.

Не всегда и всюду нужна проверка, поэтому разработчики предоставили нам средство быстрого отключения функции проверки для заданных программных блоков — атрибут {attribute 'no_check'}. Его необходимо добавить в первую строку области объявления переменных, до строк PROGRAM, FUNCTION_BLOCK или FUNCTION. Тем более, что использование автопроверки вызывает дополнительную нагрузку на процессор.


Оценка производительности


Справочная система предупреждает о дополнительной нагрузке на ПЛК при использовании функций проверки деления (а также функций проверки на выход за пределы диапазона или проверки адреса указателя). Давайте измерим эту нагрузку. Для этого я написал специальный тест производительности:

FOR c := 1 TO 1000000 DO
    a := 12 / b;
END_FOR

Управлять нагрузкой будем с помощью количества циклов FOR: от 100 000 до 1 000 000 (столбец A). Непосредственно нагрузку подсмотрим в закладке Online раздела SYSTEM. Всего необходимо рассмотреть три различных случая:
  1. Без контроля деления на ноль, выставлен атрибут "no_check" — зеленый столбец B.
  2. С включенным контролем деления на ноль — желтый столбец C.
  3. С включенным контролем деления на ноль и, через установку переменной b := 0, имитируем деление на ноль в каждой итерации цикла — красный столбец D. Предполагается максимальная нагрузка.
И сразу результат. По вертикальной оси гистограммы отражена нагрузка на процессор ПЛК в процентах (80% — заданный мною потолок для TwinCAT):


February 3, 2018

Spectre и Meltdown

После очередного обновления Windows была пропатчена на предмет уязвимостей Spectre/Meltdown. Им, если кратко, подвержены практически все современные процессоры(!) Именно процессоры, а точнее, их супер-современная архитектура. И пока все еще мутно относительно продукции AMD, с Intel и частично ARM-ом уже многое понятно.

На самом деле, все не так страшно, как трубили СМИ, да и производители операционных систем уже подсуетились и наставили заплат, зато просела производительность ЦПУ, но там интереснее другое. Нам интереснее другое, а дома решайте самостоятельно — отключать или нет.
Детектив о трех частях про уязвимости можно прочитать там: Meltdown // Spectre // часть третью ищите самостоятельно, там кажется про андроид-телефоны, что совершенно неинтересно для мира Total WINdows Control etc, но интересно для расширения кругозора.

Кстати, разобраться как вообще работают эти уязвимости — полезный перк в понимании работы компьютерного "железа".

Основная проблема в заплатках которые выкатили разработчики Windows. Так как уязвимость заключается в самой архитектуре процессора, то и патч задевает что-то важное на очень низком уровне операционной системы, а TwinCAT работает как раз там: на уровне драйверов и системных вызовов.

Кратко: проблема касается только рабочего режима 64-разрядного рантайма TwinCAT 3 на полных версиях Windows. Как только вы попытаетесь запустить локальный 64-разрядный рантайм TwinCAT 3 — тут же отхватите упаковку глюков, багов и синих экранов.

Проблема не касается:
  • Windows CE (Compact).
  • 32-x разрядных версий полноценной Windows.
  • В режиме конфигурирования (синий значок).
  • В инженерных версиях независимо от разрядности.

В общем, как обычно — отключаем этот патч и ждем очередную версию TwinCAT, где обещают решить проблему совместимости. Не нужно отключать обновления операционной системы, оставьте все как есть, просто отключите патч — об этом читайте дальше.

ЗЫ: Проблемы с запуском действительно проявляются. Проверено на живых человеках (мы бережем природу и животных).


Отключаем эти обновления совсем


Beckhoff Security Incident Team предлагает нам скачать официальный патч из спасательного набора WinPE_DeploySkript (257Мб). Распакуйте его куда-нибудь и запустите \WinPE_DeployScript\WinPeX64_Basic\media\patch.cmd от имени Администратора (правой кнопкой → запустить во имя Администратора).

Перезагрузитесь.

Аналогичное можете сделать вручную через редактор реестра и погуглив пару-тройку минут. Если же синий экран смерти все-таки настиг вас, создайте загрузочную флэшку с помощью \WinPE_DeployScript\CreatePE.bat и загружайтесь с нее. Далее скрипт patch.cmd, про который было выше.