PLC Lint // статический анализ

Разбор реальных находок

5 багов, которые компилятор не ловит

Проект в CODESYS собирается без ошибок — и уезжает на объект с невызванным таймером, усечённой уставкой и делением на переменную, которая иногда ноль. Синтаксис валиден, поведение — нет. Вот что находит статический анализ там, где компилятор молчит.

8реальных экспортов котельных
62–86score из 100
~10 секот загрузки до отчёта
0строк сохранено на диск

Тебе достался чужой проект

Писал не ты, документация — папка PDF, а человек, знавший, зачем там этот IF, уволился. Акт подписывать всё равно тебе.

  • Что код вообще делает — POU, задачи, устройства и привязки I/O на одной странице
  • Где рванёт: невызванные таймеры, недостижимые ветви, деление на переменную, которая иногда ноль
  • Что балласт — POU, которые никто не вызывает, копипаст-контуры, остатки от прошлого объекта

Спорить с NDA тут не с кем: код не твой, на диск ничего не пишется, отчёт — за десять секунд. Свой проект перед сдачей проверяется так же.

Findings — по важности

uncalled-timer high · критично

Невызванный таймер

VAR
    Pause_nasos : TOF;
END_VAR

// .Q используется в логике пуска насоса…
IF NOT Pause_nasos.Q THEN Start_Pump(); END_IF
// …но сам блок нигде не вызван — таймер не тикает,
// Pause_nasos.Q навсегда в дефолте.
Компилятор: собирается PLC Lint: поймал

Блок объявлен и читается, но не вызывается — пауза между пусками не работает. Найдено на реальном проекте котельной.

real-to-time-mul medium · внимание

Усечённая уставка через REAL_TO_TIME

tPulse := REAL_TO_TIME(rSetpoint) * 10;
// REAL_TO_TIME трактует число как миллисекунды
// и режет дробь ДО умножения — уставка уехала в мусор.
Компилятор: собирается PLC Lint: поймал

Классика «переводил секунды в миллисекунды». Домножение уже не спасает — дробь потеряна на конвертации.

div-by-zero high · критично

Деление на переменную-ноль

rAvg := rSum / iCount;
// iCount при пустой выборке = 0 → рантайм-исключение
// на объекте, а не на стенде.
Компилятор: собирается PLC Lint: поймал

Линтер различает: делитель — литеральный 0, переменная, которая никогда не присваивается ненулём, или присваивается 0 без проверки <> 0. Типо-осознанно: обычное деление REAL он не трогает — давили ложняки специально.

unreachable-branch high · критично

Недостижимая ветвь IF/ELSIF

IF x >= 100 THEN
    Alarm_Low();
ELSIF x >= 120 THEN   // недостижимо: >=120 уже поймано выше
    Alarm_High();
END_IF
Компилятор: собирается PLC Lint: поймал

Порядок порогов перепутан — верхняя авария никогда не сработает. Правило сравнивает полный правый операнд, чтобы не путать это с нормальным гистерезисом (T + 0.02).

watchdog-disabled medium · внимание

Сторожевой таймер задачи отключён

<TaskSettings KindOfTask="Cyclic" Interval="t#10ms">
    <Watchdog Enabled="false" />   <!-- на ВСЕХ задачах -->
</TaskSettings>
Компилятор: собирается PLC Lint: поймал

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

Это реальный отчёт, не макет

Настоящий вывод на синтетическом проекте с внесёнными багами. Тот же отчёт получишь на своём экспорте — score, важность, разбивка по правилам и каждая находка с конкретным фиксом.

PLC Lint report Открыть полный живой отчёт →

Новое: вторая сторона сдачи

Акт подписывает тот, кто код прочитать не может

Отчёт о качестве написан для инженера, который этот код писал. А деньги и настоящий страх — у того, кто объект принимает: он расписывается за то, чего прочитать не в состоянии, и «невызванный таймер в Main:214» ему ничего не говорит. Поэтому у того же анализа есть второе лицо — список вопросов подрядчику на человеческом языке, без кода и номеров строк. Распечатал, приложил к акту, получил ответы письменно.

Сгруппировано по цене вопроса: требуют ответа до подписания / уточнить при приёмке / замечания к сопровождению. Строится из того же отчёта — на сервере ничего не хранится.

Открыть живой протокол приёмки →

Что ещё появилось

Профиль «котельная / ИТП»Опциональное правило на аналоговые значения, которые нигде в проекте не сравниваются с диапазоном: обрыв Pt1000 читается как достоверные 0 °C, и клапан открывается на полную. Одно правило, а не шесть — остальные пять отпали после замера на девяти реальных экспортах.
Отраслевой срез«48 из 100» не значит ничего, «48 при медиане 62» значит всё. Публикуется, только когда выборка набралась: среднее по десятку файлов — не среднее.
Второе мнение перед пускомАвтоотчёт плюс письменный вывод инженера, что из найденного опасно именно у вас. Ответ за 24 часа.

Что проверяет PLC Lint

30 проверок по коду, структуре и конфигурации. Часть того, что ловит:

Это выборка — все 30 правил с фиксами на GitHub.

Прогони свой экспорт — узнай свой score

Загрузи .xml из CODESYS (Project → Export PLCopenXML), TwinCAT3, WAGO или ОВЕН. Отчёт за 10 секунд. Бесплатно, без регистрации для первого анализа, код не сохраняется.

…или посмотри живой пример отчёта →