Posted by: Harold Ennulat | April 21, 2014

PLC & HMI Tag Naming Tips

 PLC & HMI Tag Naming Tips

 And

The Impact On Rapid Code Generation

__________________________________

A Tag Naming Best Practice Suggestion

TipFor those PLC’s (programmable logic controllers or Programmable Automation Controllers) that allow the user to name each variable, I would like to suggest that as a best practice, PascalCase variable naming together with a hierarchical tag format in the form of AreaSectionEquipmentFunction be used primarily when naming variables.

So for example CheeseBelt1MotorStartPB, CheeseBelt1MotorRunCmd, CheeseBelt1MotorRunning, CheeseBelt1MotorFault, and perhaps there are 20 or 100 more CheeseBelt1.. type tags.  Here “Cheese” is the Area, Belt1 is the Section or Equipment, Motor is also a piece of Equipment (that is part of Belt1), and RunCmd is the Function.

There are many less then best examples found when doing a Google search for PLC tag naming conventions

There are many less then best examples found when doing a Google search for PLC tag naming conventions

This hierarchical form of tag naming can sometimes eliminate the need to add descriptors to the tag as the tag name is ideally self-explanatory.  This is especially true on smaller projects.  For larger projects the tags can get long and so abbreviations are often used for common names.  For example in an actual application the word for Cheese might be abbreviated “Chee” or even “C”.  In this case it might be helpful to add the word cheese to either the tag descriptor or put a list of abbreviations in a rung comment at the beginning of the program.

This hierarchical form allows for rapid code generation if say there are 2 or more Belts with the same or similar functionality.  Once the code for one Belt has been developed the code for Belt 2 is just a copy and paste of belt 1 with a search and replace of Belt1 for Belt2.  From there, Belt1 and Belt2 can be copied and pasted to make Belt3 and Belt4.  If there is a need for say 20 belts, then Belt1 through Belt9 would be copied and pasted and the word “Belt” would be searched and replaced with “Belt1” to get Belt11 – Belt19.  The more of one object that needs to be made the more efficient generating the code becomes.

This hierarchical form also allows all the variables for a particular piece of equipment to be seen by just doing an alphabetic sort of the tags.  No more hunting around to find all the variables.  Because the variables can be quickly replicated using copy, paste, and replace techniques, the tag names can stay perfectly organized.  Also if an orphan tag is generated such as CheezeBlt4…. instead of CheeseBelt4…. typos or deviations in naming become more readily apparent when browsing the tag list.

Notes on Tag Naming Standardization

I have come to prefer using the word “Run” or “Stop” to indicate a command and words ending in “ing” like “Running” or “ed” like “Stopped” to indicate status based on a PLC input. Here I used RunCmd to further clarify that this is intended to drive a PLC output.  For pushbuttons from any source including HMIs, I use the PB suffix such as StartPB, StopPB, or JogPB.  For toggles or switch inputs that are maintained, the suffixes vary somewhat but generally SW is used for ModeAutoSW or ModeManSW as functions.  (The actual mode would have the suffix of ModeAuto or ModeMan appended to the “base tag”).

Additionally most all tag names are chosen so that when the tag statement is true the tag is on or a “1” value in the PLC.  For example the RunCmd is on when the bit is a “1”, the same for “Running” or “Fault”.  The only exception might be the input for a Stop pushbutton (StopPB) as these are wired normally closed at the input of the PLC to be fail safe.  So a “0” value when the physical Stop push button is pressed will stop the device.  Often I/O are mapped to intermediate tags and in these cases the input is inverted again as it is brought into the logic so that the StopPB again will turn to a “1” when the device is to stop.  Analysis shows that this can be done without losing any fail safety, though it often isn’t immediately obvious for programmers new to this idea.

There can be many more rules applied on any particular project but the above two (2) points have been common for most all my projects where I got to decide. For larger projects it is not uncommon to start and maintain a tag dictionary for how names are constructed, a list of abbreviations and the meaning of abbreviations.  This is especially useful when 2 or more people are writing code, but can be useful for even one engineer that might need to take a break from the program.

Tagging Using Structured Tags

For PLC’s that support structured tags (also called user defined tags) the tags in the above example could also be structured as CheeseBelt1.Motor.RunCmd, CheeseBelt1.Motor.Running, CheeseBelt1.Motor.Fault, etc.  With structured tags there may only be one tag that encapsulates all the elemental tags associated with Belt1.  Therefore making a Belt2 tag is just a matter of making another instance of the structured tag that was used for Belt1.  When an element needs to be be added for Belt1 through Belt5 for example, then only the definition of the structure needs to be changed for all 5 belts to get the new elemental tag.

Structured Tag use in User Defined Instructions

Structured tags are even more powerful when they are combined with User Defined Instructions (Called AOI’s by Rockwell, DFBs by Schneider, and Blocks such as FCs by Siemens).  A CheeseBelt1 control could be contained in a single User Defined Instruction with just one structured variable called CheeseBelt1.  Making additional CheeseBelts would require just inserting a single instruction block and assigning a single structured tag for each additional belt.

This can greatly improve program development time and program maintainability.

The primary caution to using structured tags or user defined instructions/blocks is that they are not always editable when online with a running processor.  So choosing which of the above methods to use is often influenced by what kind of changes might be needed in the field to the running machine.

Another caution is that if there is a lot of debug or maintenance required, the process can be more tedious if you only have a few instances of the instruction in the program.

__________________________________

When Not To Use Hierarchical Formatted Tags

There are times however when it seems prudent to deviate from the hierarchical AreaSectionEquipmentFunction approach.  An example of this is when there is a need to write code that needs to index through an array.  In this case the variables may be defined as array elements.  However depending on the manipulation that is needed, the array elements may be structured tags.  This will allow for some level of clarity in the meaning of the tags.

Array tags generally require additional documentation to clarify the meaning of each tag element so array tag use should be limited only if special manipulation is needed that requires an index to search or loop through the array.

__________________________________

Wish List

WishListI am looking for a time when PLC software will allow some kind of enumeration capabilities in the named tags, so that arrays with numeric index numbers can be eliminated, but as of this writing I am not aware that this kind of functionality is available from any of the major PLC manufacturers.

__________________________________

Created: 04/16/2014 6:14pm CST; Edited 04/21/2014 3:44pm CST:

for Publishing on 04/21/2014 at 7:55 am CST

Advertisements

Responses

  1. Interesting. I have a very similar approach to tag names, though I usually name my areas “A#”, to keep things short. For example: A3LiftBelt2_Motor.runFwd

    To differentiate between a command and a status, I use:

    motor.doRun
    motor.isRunning

    valve.doOpen
    valve.isOpen

    “do” always indicates a command and “is” indicates a status.

    You also brought up the importance of having a tag name match the true/false value that is associated with the tag. That is why I like to use the “is” prefix; it turns the tag name into a question, which can be answered by the value of the tag, so there is never any confusion. When motor.isFaulted = 1 (true) then the motor is actually faulted, never the other way around.

    I have also found that I need two types of commands:

    motor.doRun – self-clearing (every scan)
    motor._doRun – not self-clearing

    Take, for example, a dust collector that needs to be turned on whenever any one of several different other independent pieces of equipment run. The simplest approach is to have each piece of equipment send a request command to run the dust collector. To accomplish this many-to-one command relationship, the dust collector needs a dustColl.doRun bit that can be turned on from several places in the code, corresponding to the several pieces of equipment requesting it to run. That works fine for turning it on, but what about turning it off? If one piece of equipment no longer wants it to run, should it clear the dustColl.doRun bit? What if a different piece of equipment still requires it to keep running? Scan order can really mess things up, here. The only workable approach is to have the dustColl function block clear it’s own doRun bit every scan, and then require the various pieces of equipment to continuously re-latch it whenever they want it to run. When no pieces of equipment are re-latching, the bit stays off and the dustColl stops.

    But what happens when you want to control the dusColl with a button on the HMI? The HMI can’t communicate to the PLC every scan to continuously relatch the doRun bit, the HMI communication is too slow. This is where dustColl._doRun is used instead. Whenever _doRun is set to true, it stays that way. It is not self-clearing. Once it is set the motor will continue to run until something clears it again. Make sense?

    Lastly, I’ve become fond of using “setters”. To change the value of a tag, particularly when that tag value may be altered by several different sources. This means I create tags such as:

    motor.set_VFDSpeed – property setter
    motor.VFDSpeed – property

    I think the value of setters is pretty well addressed in the general programming world so I won’t rehash it here.

    Anyway, great post. I’m glad I stumbled upon your site. It looks like we have a lot in common besides just programming.

    • It sounds like we’d have a good discussion on tagging. Let me just comment on one thing though which is about programming practice rather than tagging.

      Perhaps this is not what your meant, but why would you have something turn off the Run command bit directly? I use the “…StartPB” and “….StopPB” to latch and reset the Run command bit. If there is more then one source and it matters I have typically just added additional Start and Stop pushbuttons in parallel and series respectively.

      For the Many to One relationship you mention. Couldn’t you just parallel the running requests from each system requesting the dust collector to run and turn off the dust collector (after a delay) when the dust collector run request signal turns off? Since the dust collector request (DustCollectorRunReq) signal is on if any of the systems are running or requesting to run and off when all of the requesting systems are all off, wouldn’t this address this problem?

      To tie this into the start/stop/run circuit you can use transitional bits that are on for one scan when the DustCollectorRunReq bit first turns on or off. So a DustCollectorRunReq on transition would act like a start button and turn on the DustCollectorRun bit and a DustCollectorRunReq off transition would act like a stop button and turn the DustCollectorRun bit off. It is harder to describe then the write out the logic which I did in 3 simple rungs.

      I too would shorten the names especially for larger projects. The long name in the article is for reader clarity.

      Thanks for such a detailed response.

  2. I may not have been totally clear in my explanation. You asked why I would have something turn off the Run command bit directly. I’m guessing you are talking about the “motor._doRun” bit that I mentioned, that stays on whenever it gets set to on, and is only turned off by when it is directly cleared. Is that right? I use this mainly for HMI Start/StopPB. These soft buttons can work just like physical PBs but they can reference they can both reference the _doRun tag directly. One latches the tag on, the other unlatches it. Of course with physical push buttons you would link their physical input tags to latch and unlatch instructions for the same motor._doRun bit. Does that make sense? It is difficult to talk about code without actually writing it! I’m not sure what kind of structured text syntax you are most familiar with but here is some Beckhoff/Wago syntax, with equivalent RSLogix instructions in parenthesis:

    (OTL)
    IF InputStart THEN
    motor._doRun = 1;
    END_IF

    (OTL)
    IF InputStop THEN
    motor._doRun := 0;
    END_IF

    This works great for stop/start PB type control of a motor, but what if you also need the same motor, which is something that is often done in manual control scenarios, but often I want to run the same motor in an automatic mode, where that motor needs to be on when any one of several other pieces of equipment are running. There are two approaches to this. You mentioned the paralleling approach, which is probably the most common:

    (OTE)
    dustColl.doRun := conv1.isRunning OR conv2.isRunning OR conv3.isRunning;

    Where do you put this logic? You probably put it in with the dust collector logic.

    The other option is to separate these:

    (OTL)
    IF conv1.isRunning THEN
    dustColl.doRun := 1;
    END_IF

    IF conv2.isRunning THEN
    dustColl.doRun := 1;
    END_IF

    IF conv3.isRunning THEN
    dustColl.doRun := 1;
    END_IF

    Where do you put these? You would put one on each of the conveyors that request the dust collector to start. And at the end of the dust collector logic you would have an unlatch rung:

    (OTU)
    dustColl.doRun := 0;

    I admit that the second option seems like a lot more work, but only until you you want to wrap things up in customer function blocks (AOIs). If you created a generic conveyor function block, you would not have to rewrite the code three times, just supply a pointer to the dustColl tag. And what if you had multiple dust collectors and wanted to make a dust collector function block? Uh, oh, you can’t do that with the first “paralleling approach, as you would have this line of doRun logic that would be slightly different for every instance of the dust collection block. So, as soon as things start to scale up, the second option becomes very clean and you don’t have to write any additional logic, just provide pointers. I have made this my general approach in all cases as it makes all my function blocks more easily reusable/portable, and because it seems sooner or later most of my projects grow.

    I hope that makes sense. I could take a third stab at it if it’s still unclear.

    • Sounds like you are confident in your approach, so I’d be interested to learn more. I am unclear how you are using pointers since I am not aware that RSLogix 5000 has pointers like in the “C” programming language.

      It sounds like you are writing the Conveyor and DustCollector AOIs in such a way that no intermediate code needs to be written to interface the 2 objects. Is that correct?

      • Yes, you are correct: no intermediate code required. Everything is like Lego blocks. (Okay, not quite everything, but I try.)

        Think of the situation where you are working as part of a team of programmers and you are each given responsibility for developing code for a particular component. Bill does the dust collector, Doug does the conveyor, Suzie does the weigh hopper… Several of the modules need to request the dust collector to run, but it’s not known exactly which ones will require this when everyone starts working on the code. Bill would rather not have to collect a list of all the specific instances of the various other equipment modules that need dust collection for a particular project, particularly if the list changes several times. Instead, he is just going to provide a “dustColl.doRun” bit (an interface), and tell everyone, “Hey, if your module needs dust collection, just latch this bit.” Bill’s philosophy is: the dust collector does not care about any of the other equipment, it just needs to know when something wants it to run, and the code that makes up the dust collector module should reflect this.

        Now, I don’t actually work in a team like this, and I think programs turn out better when a single person does most, if not all of the work. That said, I try to organize my own code as if I were on a team, as if I expected the project to get so big that it would need a team, down the road. I find it helps me keep things more organized and easy to edit down the road. It also makes code reuse much easier – many of my projects are variations on a theme so it is very nice to grab a similar one and just delete unneeded routines/modules and know all the code for that module is there and nowhere else.

        About the RSLogix pointers: I’m referring to InOut parameters in AOIs. Of course, they are not the wild-and-free-roaming pointers of C++; they are just your garden variety parameters passed by reference, with type-checking, but they are essentially pointers. When I create a conveyor AOI, I create it with an InOut tag of TYPE dustColl. Then, when I use a conveyor block, I can pass it a reference to a specific instance of dustColl tag, for example, “A1DustColl3”. All the “linking” code is already built-in to the conveyor block AOI. Or, since I really only care to latch the .doRun bit on the dustCollector fan motor tag, maybe I would create the conveyor AOI with just an InOut tag of TYPE BOOL, and pass it “A1DustColl3_fan.doRun”. Same idea either way.

        • Thanks for sharing your approach. It is causing me to compare this with my own approach. Ryan, you may well be the inspiration that generates another blog article. If you care to contact me directly my email can be found if you google my full name. There are only 2 of us with this name.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

%d bloggers like this: