бесплатный quality-report для экспортов PLCopenXML
← проверить другой файл

Отчёт: DemoBoiler

demo.xml · версия н/д · POU: 4 · глобалов: 0
👋 Это демо-отчёт на синтетическом проекте — чтобы показать, что вы получите на выходе. Свой код грузить не нужно. Хотите проверить свой?
🔎 1 POU на графических языках (CFC 1) — построчные проверки к ним НЕ применялись; имена покрыты частично (вхождения в блоках/выражениях). Это ограничение инструмента, а не признак чистоты этих POU.
0
Оценка качества: 0/100
Сохраните отчёт в профиль — соберётся подтверждённый трек качества. Код не сохраняется, только агрегат.
Проверить свой экспорт
7
Критично
9
Внимание
7
Мелочи

Распределение по важности

Критично 7 Внимание 9 Мелочи 7

Топ правил

naming 4
div-by-zero 3
implicit-check-disabled 3
dead-pou 3
cfc-div-zero 1
broken-implicit-check 1

У каждого правила есть кнопка шум? — нажмите её, если это правило даёт нерелевантные срабатывания на вашем коде. Сигнал помогает нам подстроить проверки: понизить важность, сузить условие или отключить лишнее. Ваш код при этом не отправляется.

Критично 7 шт.

CFC: деление на литеральный ноль 1 · cfc-div-zero
В графике (CFC/FBD) делитель блока DIV/MOD подключён к литеральной константе 0 — рантайм-исключение при исполнении блока. Проверяется по графу связей.
Варианты решения
  1. Подайте на вход делителя переменную/уставку вместо константы 0 — обычно это забытая заглушка после отладки.
  2. Если делитель может обнуляться в рантайме — поставьте перед DIV/MOD выбор безопасного значения (SEL по сравнению с 0).
  • CfcDiv — делитель блока DIV подключён к литеральному 0 → гарантированное деление на ноль
Функция неявного контроля не защищает 1 · broken-implicit-check
Функция неявного контроля (Check…) есть, но её защитная логика выхолощена: пустое тело или убрана проверка (делителя на 0 / границ). Рантайм вызывает её, но она ничего не ловит — ложное чувство безопасности, опаснее её отсутствия.
Варианты решения
  1. Верните защитную логику: CheckDivReal должен проверять IF divisor = 0 THEN … := 1;
  2. CheckBounds/CheckRange… должны зажимать значение в lower..upper. Сверьтесь с дефолтной реализацией CODESYS.
  • CheckBounds — нет сравнения с границами lower/upper — значение не ограничивается — защита есть, но не работает (ложное чувство безопасности)
Деление на литеральный ноль 2 · div-by-zero
Деление или MOD на литеральный ноль — гарантированное исключение исполнения (division by zero) при заходе в эту строку.
Варианты решения
  1. Уберите деление на 0 — вероятно, опечатка в делителе.
  2. Если делитель бывает нулём динамически — защитите: IF d <> 0 THEN r := x / d; END_IF
  • DivZeroVar:1 — деление на znever — переменная = 0 (инициализатор 0/пустой) и нигде не присваивается → гарантированное деление на ноль
    qa := 5 / znever;
  • Main:2 — деление/MOD на литеральный ноль
    d := d / 0;
Опечатка в имени глобала (gvl.*) 1 · undeclared-global
Ссылка gvl.<имя> не находит объявления в GVL — почти всегда опечатка; переменная молча создаётся или код не компилируется.
Варианты решения
  1. Сверьте написание с объявлением в GVL (регистр, подчёркивания, цифры).
  2. Если переменная действительно нужна — добавьте её в GVL с типом и комментарием.
  • Main — gvl.missing — нет в GVL (вероятная опечатка)
Индекс за границами массива 1 · array-bounds
Константный индекс выходит за объявленные границы ARRAY[lo..hi] — заход в эту строку гарантированно читает/пишет чужую память. Без активных неявных проверок рантайм это не перехватит. Проверяются одномерные массивы с числовыми границами и литеральным индексом.
Варианты решения
  1. Приведите индекс в диапазон объявления или расширьте границы массива.
  2. Перед доступом проверяйте: IF idx >= LO AND idx <= HI THEN arr[idx] … END_IF
  • Main:1 — arr[5] вне границ [0..3]
    arr[5] := 1;
Бесконечный цикл без выхода 1 · infinite-loop
WHILE TRUE / REPEAT … UNTIL FALSE без EXIT и RETURN внутри — скан никогда не завершится: задача блокируется и срабатывает сторожевой таймер (а если он выключен — встаёт контроллер). В ПЛК длинную обработку разносят по сканам, а не крутят в цикле.
Варианты решения
  1. Добавьте условие выхода и EXIT, либо ограничьте счётчиком итераций.
  2. Тяжёлую обработку разбивайте по сканам — бесконечный цикл роняет watchdog задачи.
  • Main:11 — WHILE TRUE без EXIT/RETURN

Внимание 9 шт.

Деление на литеральный ноль 1 · div-by-zero
Деление или MOD на литеральный ноль — гарантированное исключение исполнения (division by zero) при заходе в эту строку.
Варианты решения
  1. Уберите деление на 0 — вероятно, опечатка в делителе.
  2. Если делитель бывает нулём динамически — защитите: IF d <> 0 THEN r := x / d; END_IF
  • DivZeroVar:3 — деление на zcond — переменной присваивается 0 на одной из веток и нет защиты zcond <> 0 → возможное деление на ноль
    qb := 5 / zcond;
Целочисленное деление (потеря дроби?) 1 · int-division
Деление целого на целую константу отбрасывает дробь. Нужно /N.0 или *_TO_REAL, если важна точность.
Варианты решения
  1. Приведите к REAL: r := INT_TO_REAL(x) / N.0;
  2. Или делите на вещественную константу: x / 10.0 вместо x / 10.
  • Main:3 — Целочисленное деление (потеря дроби?)
    iq := iSum / 10;
Таймер с нулевой выдержкой (PT := T#0) 1 · timer-zero-pt
PT задан нулём (T#0S) — таймер срабатывает в тот же скан, выдержки фактически нет. Обычно забытая или ошибочная уставка времени.
Варианты решения
  1. Задайте ненулевую выдержку: tmr(IN := cond, PT := T#5S);
  2. Если задержка не нужна — уберите таймер и работайте по условию напрямую.
  • Main:4 — PT := T#0 — таймер срабатывает мгновенно (нет выдержки)
    t1(IN := c, PT := T#0S);
Присваивание переменной самой себе 1 · self-assignment
Строка вида `x := x;` — присваивание переменной самой себе: не делает ничего. Обычно опечатка в правой части (имелась в виду другая переменная) или остаток от удалённой логики.
Варианты решения
  1. Вероятно, справа должна быть другая переменная (опечатка) — проверьте намерение.
  2. Если строка лишняя — удалите её.
  • Main:6 — присваивание самой себе — нет эффекта
    x := x;
Пустая ветвь IF/цикла 1 · empty-branch
Ветвь IF/ELSIF/ELSE или тело цикла пусты (`THEN END_IF`, `DO END_FOR`) — логику либо не дописали, либо вырезали при правке и забыли убрать конструкцию.
Варианты решения
  1. Заполните ветвь логикой или удалите пустой IF/цикл.
  2. Если пусто намеренно — оставьте комментарий-пояснение.
  • Main:7 — пустой IF … THEN … END_IF
CASE без ветви ELSE (нет default) 1 · case-without-else
CASE без ветви ELSE: если селектор примет значение, которого нет среди меток, не выполнится ничего — молчаливый пропуск (частый баг стейт-машин).
Варианты решения
  1. Добавьте ветвь ELSE с обработкой по умолчанию (сигнал/лог/безопасное состояние).
  2. Если все значения селектора точно покрыты — оставьте ELSE с комментарием-пояснением.
  • Main:8 — CASE без ELSE — неучтённые значения селектора молча игнорируются
Проверка присутствует, но не активна 3 · implicit-check-disabled
Функция контроля есть в проекте, но не активна: либо не зарегистрирована (нет метки CheckFunction — рантайм её не вызывает), либо для целой категории (границы/деление/диапазон/указатели) нет ни одной активной проверки.
Варианты решения
  1. Перегенерируйте POUs for Implicit Checks штатным диалогом CODESYS — так проверка получит регистрацию CheckFunction и будет вызвана рантаймом.
  2. Добавьте недостающую категорию (границы/деление/диапазон/указатель) через Add Object → POUs for Implicit Checks.
  • проект — рантайм не проверяет «деление на ноль» — нет активной функции контроля этой категории
  • проект — рантайм не проверяет «выход за диапазон» — нет активной функции контроля этой категории
  • проект — рантайм не проверяет «разыменование указателя» — нет активной функции контроля этой категории

Мелочи 7 шт.

Мёртвый код — POU нигде не вызывается 3 · dead-pou
POU нигде не вызывается и не используется как тип — мёртвый код либо забытая правка имени (сверьте с ТЗ, прежде чем удалять).
Варианты решения
  1. Не нужен — удалите POU.
  2. Забытая версия — сверьте имя вызова с ТЗ, возможно опечатка в имени.
  • CfcDiv — CfcDiv — не вызывается и не используется как тип
  • DivZeroVar — DivZeroVar — не вызывается и не используется как тип
  • Main — Main — не вызывается и не используется как тип
Криптовое имя переменной 4 · naming
Односимвольные имена (кроме счётчиков i/j/k/n) нечитаемы при сопровождении. I/O-теги и пины FB не трогаем.
Варианты решения
  1. Дайте осмысленное имя по назначению (напр. iRetryCount вместо n).
  • CheckBoundsx — односимвольное имя, дайте осмысленное
  • Maind — односимвольное имя, дайте осмысленное
  • Mainx — односимвольное имя, дайте осмысленное
  • Mainc — односимвольное имя, дайте осмысленное
Чего линтер НЕ видит (нужен глаз и сверка с ТЗ): полярность сигналов, перепутанный гистерезис (< set+gist / > set−gist), copy-paste чужого контура. Отчёт — повод перепроверить, а не приговор.
Обработка идёт в памяти — загруженный код не сохраняется. Инструмент советует; правки по коду принимает инженер (см. что линтер не видит). · Обратная связь · GitHub