Случайные ошибки
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. Всего необходимо рассмотреть три различных случая:
- Без контроля деления на ноль, выставлен атрибут "no_check" — зеленый столбец B.
- С включенным контролем деления на ноль — желтый столбец C.
- С включенным контролем деления на ноль и, через установку переменной b := 0, имитируем деление на ноль в каждой итерации цикла — красный столбец D. Предполагается максимальная нагрузка.
И сразу результат. По вертикальной оси гистограммы отражена нагрузка на процессор ПЛК в процентах (80% — заданный мною потолок для TwinCAT):
No comments
Post a Comment
Note: Only a member of this blog may post a comment.