Проект в CODESYS собирается без ошибок — и уезжает на объект с невызванным таймером, усечённой уставкой и делением на переменную, которая иногда ноль. Синтаксис валиден, поведение — нет. Вот что находит статический анализ там, где компилятор молчит.
8реальных экспортов котельных
62–86score из 100
~10 секот загрузки до отчёта
0строк сохранено на диск
Тебе достался чужой проект
Писал не ты, документация — папка PDF, а человек, знавший, зачем там этот IF, уволился. Акт подписывать всё равно тебе.
Что код вообще делает — POU, задачи, устройства и привязки I/O на одной странице
Где рванёт: невызванные таймеры, недостижимые ветви, деление на переменную, которая иногда ноль
Что балласт — POU, которые никто не вызывает, копипаст-контуры, остатки от прошлого объекта
Спорить с NDA тут не с кем: код не твой, на диск ничего не пишется, отчёт — за десять секунд. Свой проект перед сдачей проверяется так же.
Findings — по важности
uncalled-timerhigh · критично
Невызванный таймер
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-mulmedium · внимание
Усечённая уставка через REAL_TO_TIME
tPulse := REAL_TO_TIME(rSetpoint) * 10;// REAL_TO_TIME трактует число как миллисекунды// и режет дробь ДО умножения — уставка уехала в мусор.
✓Компилятор: собирается⚠PLC Lint: поймал
Классика «переводил секунды в миллисекунды». Домножение уже не спасает — дробь потеряна на конвертации.
div-by-zerohigh · критично
Деление на переменную-ноль
rAvg := rSum / iCount;
// iCount при пустой выборке = 0 → рантайм-исключение// на объекте, а не на стенде.
✓Компилятор: собирается⚠PLC Lint: поймал
Линтер различает: делитель — литеральный 0, переменная, которая никогда не присваивается ненулём, или присваивается 0 без проверки <> 0. Типо-осознанно: обычное деление REAL он не трогает — давили ложняки специально.
unreachable-branchhigh · критично
Недостижимая ветвь IF/ELSIF
IF x >= 100 THEN
Alarm_Low();
ELSIF x >= 120 THEN// недостижимо: >=120 уже поймано выше
Alarm_High();
END_IF
✓Компилятор: собирается⚠PLC Lint: поймал
Порядок порогов перепутан — верхняя авария никогда не сработает. Правило сравнивает полный правый операнд, чтобы не путать это с нормальным гистерезисом (T + 0.02).
watchdog-disabledmedium · внимание
Сторожевой таймер задачи отключён
<TaskSettings KindOfTask="Cyclic" Interval="t#10ms"><Watchdog Enabled="false" /> <!-- на ВСЕХ задачах --></TaskSettings>
✓Компилятор: собирается⚠PLC Lint: поймал
Не баг сам по себе — но для критичного процесса зависший цикл никто не перезапустит. Стоит увидеть в сводке, а не узнать на объекте.
Это реальный отчёт, не макет
Настоящий вывод на синтетическом проекте с внесёнными багами. Тот же отчёт получишь на своём экспорте — score, важность, разбивка по правилам и каждая находка с конкретным фиксом.
Отчёт о качестве написан для инженера, который этот код писал. А деньги и настоящий страх — у того, кто объект принимает: он расписывается за то, чего прочитать не в состоянии, и «невызванный таймер в Main:214» ему ничего не говорит. Поэтому у того же анализа есть второе лицо — список вопросов подрядчику на человеческом языке, без кода и номеров строк. Распечатал, приложил к акту, получил ответы письменно.
Двум и более модулям назначен один адрес на шине — какой из них отвечает на опрос?
У задачи отключён сторожевой таймер — кто перезапустит её, если скан переполнится?
Есть условия, которые не выполнятся ни при какой температуре — что задумывалось в этих режимах?
Таймер объявлен, его выход используется, но сам он не запускается — выдержка работает?
Сгруппировано по цене вопроса: требуют ответа до подписания / уточнить при приёмке / замечания к сопровождению. Строится из того же отчёта — на сервере ничего не хранится.
Профиль «котельная / ИТП»Опциональное правило на аналоговые значения, которые нигде в проекте не сравниваются с диапазоном: обрыв Pt1000 читается как достоверные 0 °C, и клапан открывается на полную. Одно правило, а не шесть — остальные пять отпали после замера на девяти реальных экспортах.
Отраслевой срез«48 из 100» не значит ничего, «48 при медиане 62» значит всё. Публикуется, только когда выборка набралась: среднее по десятку файлов — не среднее.
Второе мнение перед пускомАвтоотчёт плюс письменный вывод инженера, что из найденного опасно именно у вас. Ответ за 24 часа.
Что проверяет PLC Lint
30 проверок по коду, структуре и конфигурации. Часть того, что ловит:
Необъявленное имя — опечатки в gvl.*
Таймеры/триггеры, которые читаются, но не вызываются
Деление на ноль — литерал или переменная, всегда равная 0
Недостижимые ветви IF/ELSIF (пороги в неверном порядке)
Загрузи .xml из CODESYS (Project → Export PLCopenXML), TwinCAT3, WAGO или ОВЕН. Отчёт за 10 секунд. Бесплатно, без регистрации для первого анализа, код не сохраняется.