← Resources
Instructions·7 min read·

User defined types: naming the thing instead of the address

What a UDT buys, how to decide what goes in one, and the versioning problem on a live system.

Short answer

A user defined type is a structure that groups related tags under one name, so a valve becomes a single tag with members for its command, its feedback, its fault and its timer. It makes the tag table describe the plant rather than the memory map, makes an array of forty identical devices trivial, and centralises a change. The cost is that changing the type on a running system usually requires a download.

How wide each type isBOOLone bitINT-32,768 to 32,767DINT±2.1 billionREALfloating pointA DINT holding a BOOL wastes 31 bits, and nobody notices until the tag count does.

A tag table is either a description of the plant or a description of the memory map, and which one it is decides how much of a new engineer's first month is spent working out what things are called.

What a UDT is

A structure. A named type containing named members, instantiated as many times as needed.

Define ValveType with members Open_Cmd, Closed_FB, Opened_FB, Fault, Travel_Timer. Create V101 of that type. Now V101.Fault is meaningful to somebody who has never seen the program.

The alternative is V101_Open_Cmd, V101_Closed_FB and so on as separate tags, where the relationship between them exists only in the naming convention and holds until somebody types an underscore in the wrong place.

What it buys

Arrays become possible. Forty valves is Valves[40] of ValveType, and one routine handles any of them by index. Without the type, forty valves is two hundred loose tags.

A change lands everywhere. Adding a stroke counter to every valve is one edit to the type rather than forty edits to forty tags.

The tag table reads as documentation. Browsing V101 shows what a valve consists of.

HMI and reporting get simpler. A faceplate binds to a valve rather than to five separate tags whose names it has to construct.

Deciding what goes in one

The test: does every instance need this member?

A member that applies to two of forty valves does not belong in the type. It ends up present and unused on thirty eight instances, and the next person to read the type cannot tell it is exceptional.

Where a few instances genuinely need more, either nest a second type, or hold the exceptional data separately and index it alongside.

The other failure is a type with sixty members covering every valve anybody might ever fit. It is not wrong and it is a large amount of memory times forty, and every one of those members appears in every cross reference forever.

Nesting

Types can contain types, and it is useful up to about two levels.

Station containing Valves[4] of ValveType and Motor of MotorType reads well and matches how the plant is described.

Line.Station[3].Valve[2].Timer.ACC is four levels and is where it stops helping, because nobody can hold the path in their head while looking at a rung.

The versioning problem

The one real cost, and it is worth planning for rather than discovering.

Adding a member changes the memory layout of every instance. Most controllers cannot do that online, so the change means a download, and a download means the plant stops.

Three approaches, none of them elegant.

  • Get it right first. Realistic on a new machine where the design is understood.
  • Leave spares. A few reserved members in the initial definition. Ugly, effective, and it is what experienced people do on systems that cannot be stopped.
  • Plan the downtime. Batch structural changes and take them at a scheduled stop rather than one at a time.

Naming

Two conventions worth adopting because they pay for themselves.

Name the type after the thing, not the project: ValveType, not Line3Valve. Types get copied between projects and a project specific name follows them for years.

Name members after what they mean rather than how they are wired: Opened_FB rather than DI_04. The wiring is on the drawing and will change; the meaning will not.

Common questions

When should I create a UDT?
When the same group of tags appears more than twice for the same kind of thing. Two valves is a judgement call; six valves each with a command, two feedbacks, a fault and a timer is a UDT, because the alternative is thirty six loose tags whose relationship exists only in the naming convention.
Can I change a UDT on a running controller?
Usually not without a download. Adding a member changes the memory layout of every instance, and most controllers cannot do that online. Leaving spare members in the definition from the start is a common and slightly ugly workaround for systems that cannot be stopped.
What is the difference between a UDT and an add-on instruction?
A UDT is data with no behaviour. An add-on instruction is data plus the logic that operates on it, packaged and versioned together. Use a UDT when different instances need different logic, and an AOI when the logic is genuinely the same everywhere.

Keep reading