<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Recent posts to Checking the device tree, not just the code: what else is worth a static check?</title><link>https://forge.codesys.com/forge/talk/Engineering/thread/8a6dfb2f05/</link><description>Recent posts to Checking the device tree, not just the code: what else is worth a static check?</description><language>en</language><lastBuildDate>Tue, 27 Oct 2026 08:10:53 -0000</lastBuildDate><atom:link href="https://forge.codesys.com/forge/talk/Engineering/thread/8a6dfb2f05/feed.rss" rel="self" type="application/rss+xml"></atom:link><item><title>Checking the device tree, not just the code: what else is worth a static check?</title><link>https://forge.codesys.com/forge/talk/Engineering/thread/8a6dfb2f05/?limit=25#b2d2</link><description>&lt;div class="markdown_content"&gt;&lt;p&gt;I spend most of my time reviewing other people's CODESYS projects before commissioning,&lt;br/&gt;
and two kinds of defects keep showing up. Both compile clean. Both read fine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;One is in the code.&lt;/strong&gt; A pause timer whose own input gets cleared inside the branch&lt;br/&gt;
the timer is supposed to gate:&lt;/p&gt;
&lt;div class="codehilite"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;rampDelay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;run_2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kr"&gt;IF&lt;/span&gt; &lt;span class="n"&gt;NOT&lt;/span&gt; &lt;span class="n"&gt;run_2&lt;/span&gt; &lt;span class="kr"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;rampDelay&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Q&lt;/span&gt; &lt;span class="kr"&gt;THEN&lt;/span&gt;
    &lt;span class="n"&gt;run_1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;run_2&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;timer&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;own&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cleared&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="n"&gt;here&lt;/span&gt;
&lt;span class="kr"&gt;ELSE&lt;/span&gt;
    &lt;span class="n"&gt;run_1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;run_2&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;FALSE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;END_IF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Next scan IN is FALSE, the timer resets, the condition is true again, and the second&lt;br/&gt;
pump starts immediately. A 30-second ramp delay that actually lasts one task cycle.&lt;br/&gt;
The setpoint is on the HMI, the operator turns it, and nothing changes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The other is not in the code at all.&lt;/strong&gt; On one site the outdoor temperature read 0.0 °C&lt;br/&gt;
in mild weather. The ST was correct, the variable was bound to the channel — but the&lt;br/&gt;
channel's sensor type was left at "disabled", so nothing was ever digitised. Same class:&lt;br/&gt;
a module with Disable_module = TRUE, or an analog output bound to a variable while its&lt;br/&gt;
mode is off. Nothing in the project reports any of this, and the code review never looks&lt;br/&gt;
there, because the configuration lives in the device tree and not in the POUs.&lt;/p&gt;
&lt;p&gt;That second class is what pushed me to parse the &lt;code&gt;.export&lt;/code&gt; format rather than PLCopenXML&lt;br/&gt;
alone — the export carries the device tree and the visualization, so "nothing ever writes&lt;br/&gt;
this alarm variable" stops being a guess and becomes a fact you can check against the&lt;br/&gt;
screen bindings.&lt;/p&gt;
&lt;p&gt;Three things about the format that cost me time, in case they save you some:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the same device parameter can appear three times (template, internal copy, actual&lt;br/&gt;
  configuration) — the &lt;strong&gt;last&lt;/strong&gt; occurrence is the one in effect;&lt;/li&gt;
&lt;li&gt;the enum value of a parameter is not in the parameter itself, it is one level down&lt;br/&gt;
  in &lt;code&gt;BaseType/Value&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;a channel counts as used when a variable is bound to &lt;code&gt;SensorValueN.SensorValue&lt;/code&gt; —&lt;br/&gt;
&lt;code&gt;InputStatus&lt;/code&gt; is a bad indicator, people deliberately bind it on spare inputs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So the question. &lt;strong&gt;What else would you check automatically, given both the code and the&lt;br/&gt;
device tree?&lt;/strong&gt; I am collecting rules from sites, not from style guides, and the&lt;br/&gt;
configuration side feels largely unexplored.&lt;/p&gt;
&lt;p&gt;For what it is worth, the checker I use for this is free and runs offline (single exe,&lt;br/&gt;
no install, nothing uploaded anywhere): &lt;a href="https://github.com/caspershes/plc-lint" rel="nofollow"&gt;https://github.com/caspershes/plc-lint&lt;/a&gt; — 43 rules&lt;br/&gt;
at the moment, reads &lt;code&gt;.export&lt;/code&gt; and PLCopenXML. Happy to add rules people here suggest.&lt;/p&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">caspershes</dc:creator><pubDate>Tue, 27 Oct 2026 08:10:53 -0000</pubDate><guid isPermaLink="false">https://forge.codesys.comee26d5c6932b4c07a1b4da6baf8b8d5d7b3fb4b3</guid></item></channel></rss>