Name a few things that software developers always have strong opinions about and naming conventions on code is one of the things that surely will come up. What lengths for identifiers should we use? Should we use hungarian notation? CamelCase or under_score case? While programming guidelines covering these topics have been existing since the rise of the early programming languages, they are much less common for industrial control.
Initially the only one that I found was Beckhoffs for TwinCAT3. Most of Beckhoffs libraries and example code used this, even though even Beckhoff themselves have not been consistent. More recently PLCopen have released their coding guidelines written specifically for IEC61131-3. Just out of curiosity I decided to compare the two standards and their take on naming conventions, and comparing them to my own personal opinions.
A quote that I often come to think of when you having many (and often colliding) standards is:
The nice thing about standards is that you have so many to choose from – Andrew S. Tanenbaum
The PLCopen guidelines mandate that any scheme can be used for prefixes, as long as the notation is documented. Beckhoff however have created rules for the name prefixes, which makes sense since they want to have common names for their own libraries. While PLCopen leaves it open to the developer to decide for the naming conventions, they have provided some examples of naming prefixes of different types, which in turn is a reference to the 3rd edition of IEC61131-3. If we look at the case for prefixes of variables they are:
Type | Beckhoff | PLCopen |
---|---|---|
ENUM | e | e |
ARRAY | a | a |
STRUCT | st | st |
REFERENCE | ref* | ref |
FUNCTION BLOCK | fb | fb |
PROGRAM | prg | |
BOOL | b | x |
SINT | n | si |
INT | n | i |
DINT | n | di |
LINT | n | li |
USINT | n | usi |
UINT | n | ui |
UDINT | n | udi |
ULINT | n | uli |
REAL | f | r |
LREAL | f | lr |
TIME | t | tim |
LTIME | t | ltim |
DATE | d | dt |
LDATE | d | ldt |
TIME_OF_DAY | td | tod |
LTIME_OF_DAY | td | ltod |
DATE_AND_TIME | DT | dt |
LDATE_AND_TIME | DT | ldt |
STRING | s | str |
WSTRING | ws | wstr |
CHAR | N/A | c |
WCHAR | N/A | wc |
BYTE | n | by |
WORD | n | w |
DWORD | n | d |
LWORD | n | lw |
* only if not used as method input, in which case it shall be prefixed according to the basic data type of the reference
PLCopen also show examples for prefix of scope:
Scope | Prefix |
---|---|
Global scope | g |
Local scope | l |
POU Parameter | p |
Temporary varaible | tmp |
And of control:
Control | Prefix |
---|---|
Input | i |
Output | o |
However, PLCopen mentions:
Attributes can be omitted if the Programming Support Environment clearly shows them by other means, for instance by hovering over it.
Which is my personal opinion, as the TwinCAT XAE (Visual Studio IDE) clearly shows the type of a variable by just hovering over it.
In the screenshot we do not only clearly see the type (BOOL) but also the complete instance search path, what type of variable it is (instance, input, output) and the accompanying comment to the variable. This is a much clearer than for example putting “lin” in front of all integer and bit-based local input variable numbers, which is why I’ve started to avoid using prefixes altogether.
Regarding the use of case (capitals) then again PLCopen doesn’t mandate any particular standard other than that the use of capital letters in object names should be clear and consistent across the project. They do as a guideline however propose that:
- UPPER_SNAKE_CASE should be used for constants, user defined types and keywords
- Use UpperCamelCase for all other multi-word items
The Beckhoff rules doesn’t specify that that constants should be UPPER_CASE, but are on the same track with CamelCasing, let me quote:
If an identifier is made up of several words, the first letter of each word is capitalized (CamelCase). Usually, no delimiters such as ‘_’ are used between the words.
Personally I was all in for UpperCamelCase, but have started to doubt this since I decided to not let my opinions and feelings decide what I prefer, but rather let science speak. Considering the amount of developers around the world, someone must have done some science on this? I found this paper that in the abstract concludes:
Results indicate that camel casing leads to higher accuracy among all subjects regardless of training, and those trained in camel casing are able to recognize identifiers in the camel case style faster than identifiers in the underscore style.
“All fine then” I thought, but then I found a blog post that in the discussion puts forward some convincing arguments of why the results of the paper are flawed. At least the nerd inside of me got some fun reading on this topic.
At the end of the day, what matters are not the actual rules but rather that there are rules in a project and that they are used consistently followed. I would highly recommend you to read the PLCopen coding guidelines as they provide a good deal of information not only on naming conventions but more importantly on coding practices.
What are you personal preferences? Do you follow the Beckhoff or PLCopen naming conventions or do you have your own?
An interesting post. I always encourage my clients to adopt a documented coding style, and have written many on their behalf. I often have to write one I do not entirely agree with, but having consistency is generally more important than anything else.
I use camel case for all non-constant private variables, methods and properties, and upper camel case (which I refer to as pascal case) for all non-constant public.
I also use upper snake for constants.
I do make exceptions for property backing fields, where they exist, which are always prefixed with an underscore.
Other prefixes I use in definition: Classes get a “T_”, Interfaces an “I”, Enums an “E_”, Function Blocks (differentiated from classes by implementation style) get an “FB_” and Functions an “F_”.
If declaration, my public / private naming rules are used. I’m also a firm believer in well named variables, so maintainers can clearly see the intent of the software throughout.
I’m generally against any kind of declarative encoding of type into variable names. They add clutter through the full software lifecycle. In Industrial Automation, surely everyone is now using an IDE that has some form of “intellisense” (auto-complete, hover-reveal, etc) too!
Where clients have adopted my proposals wholesale, they have always been happier with the result. Their support staff, who are generally not software designers, find the resulting code easier to read.
I keep coming back to this phrase, by John F Wood, in 1991 :
“Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live. Code for readability.”
Here’s the Codesys guidelines:
https://help.codesys.com/webapp/_cds_identifiers;product=codesys;version=3.5.14.0
The company I work in now uses prefixes for data types. b – bool, i – int, r – real…
I have to do that now, but I doubt there is significant advantage in doing that.
I have worked on some software from large system integrators, which used many globals, but used prefixes to differentiate between functions of variables.
For example, B_ for button, AI_ for analog inputs, SM for state machine functions, MAIN for main functions of parts of process, etc..
It actually worked! Even though that software had almost all data declared globally(which I don’t propose), it was easy to read, understand and quick to modify.
However, I know this company had done 50+ similar projects, so the had many chances to improve and refine it. You may not get the same results if you’re doing a one off project.
Thank you,That’s great. I think we can put it on GitHub and let more people know about it, because industrial programming is such a mess.
The link to the PLCopen guidelines returned a 404…
I like your content and TcUnit, btw! As I am a SW Engineer without a real automation background, I share your opinion that TDD for TwinCAT is a real addition to the field.
Great post! I agree to any convention as far it is consistent. I would like to add that there should also be a convention for variable naming between PLC, HMI, electric diagrams, documentation, specs, … So that the conveyor is not ”Left belt” ”Infeed” ”Supply conveyor” etc. 😊
Great post.
Very nice post!
This seems to be a big flaw by the ”early days” programmers that’s still in the game. They tend to stay by the rules from the past where you only had a certain amount of characters to use and datastructures were’nt that developed.
I’ve only been working primarily within ABB ControlBuilder M and Simatic TIA Portal when programming PLC, but found my way through the guidelines and understood the purpouse and the essence of having these prefixes. This is as mentioned in the begining something that runs through all highlevel programming. In my case I brought my experience from robotics programming, ABB RAPID, where the prefix is an important part.
But the prefix it self isn’t the key to a code that could be read and understood at first glance, it’s also the name of the variable.
There should be a guideline where you have to ask yourself as a programmer:
1. What am I controling when using a structure? Name it based on what your’e controlling. E.g a structure where you have variables to control a graphical behavior of lines in a HMI. Preferrably ”LineControl”
2. What will LineControl actually controll? E.g graphical Line state, Recipe number, edge memorys and so on.
3. When using constants, declare them as such and don´t use digits if the digit has a purpouse. Name the constant based on the purpouse not only e.g ”CONST_ONE”.
My point is that the prefixes isn’t the main issue in programming when talking about industrial controllers. The main problem is that the naming of the variables are very shortned or not readable.
I don’t know about ABB, but KUKA’s name is limited to 22 characters. This is not much, but in 99% of cases it is enough for a concise explanation of a variable or function.
Over time, I realized that in an industrial robot you need to have 2 Cod Style variables: a short one for touch up from the control panel and a long one for the IDE
Only strict adherence to style allows the team to work together. “”Snowflake people” must obey a single standard.
I would also like to add that Beckhoff has a tool called “Static Analysis” that is useful for defining your own naming conventions and letting the tool find errors in which conventions are not followed. I don’t use it enough but seeing that a tool existed persuaded me to look at the PLC open guidelines and construct rules that Static Analysis could check for in my code!
I wrote about this tool already all the way back in 2017. I’d say that of the current TwinCAT 3 version (3.1.4024.25), this tool is still to buggy to be used seriously, which is crazy considering that it was released five years ago. I might write an update on that blog post in the future.
It is funny how PLC coding conventions, differ from high level code coding conventions.
The code convention is in principle The Hungarian notation. which is not done in languages like C#
in my opinion this is a relic of the past. The reason that this notation has been ‘abandoned’ in modern languages is that is makes variables less readable, and in modern IDE’s like VS there it has no function anymore. So although I am in favor of coding guidelines, I am not so much in favor of Hungarian notation.
I fully agree with you, though the integration of CODESYS/IEC61131-3 in Visual Studio is not exactly where I would want it to be to fully get rid of the hungarian notation. I guess the reason PlcOpen have their recommendations is of historical reasons, as most IDE’s for industrial automation are not anywhere near where the counterparts for traditional software development are.
.NET annoyingly still use the I prefix for interfaces, which boggles the mind. The language’s terrible extends/implements syntax is likely the culprit, taking their syntax from C++.
Beckhoff examples show Hungarian notation for lots of their .NET examples. The .NET 6.0 examples don’t though so maybe they’re seeing a bit of a change internally.
Personally, I use Java/.NET mixture. Being my personal opinion, it’s clearly the right one haha, so here it is.
lowerCamelCase for variables, method/function parameters, struct members, prefix variables with _ when it clashes with the property name (argghh why isn’t it case sensitive!!!??)
UpperCamelCase (PascalCase) for function blocks, methods, properties, functions, programs, types, library namespaces
UPPER_SNAKE_CASE for constants and enum members
I use the I_ prefix with UpperCamelCase for interfaces only because TwinCAT has very different behaviour for interfaces passed into a method/function (automatically by reference) versus as a value copy for function blocks.
Pretty silly behaviour really, making it different for interfaces versus function blocks. I can only presume because it’s functionality added as an afterthought, it was done that way because it was the path of least change to the rest of the codebase.
I don’t use TwinCATs folder structure either. I use Lib, and then a folder structure representative of the division of the code. Types and function blocks and functions that are related go together in a folder. It means you can work from a single folder when working on a single concern.
I then use a Programs folder for the Main program (and I use UpperCamelCase, not SHOUTYCASE) and any other programs if there’s more than one. I usually have a second program for the old PLC HMI. Behavioural code and data mediation is handled there.
I avoid GVLs where possible as I don’t see what benefit they offer over putting them in the Main program.