08 — PROPERTY

A member that looks like a variable and runs code — getters, setters, and the scan time they can cost.

Estimated 23 minutes.

A public variable cannot say no

SKID-207 heats a caustic jacket. Its setpoint lives in a plain VAR_OUTPUT that the faceplate writes straight into, because that was the fastest thing that worked during commissioning. On a Sunday an operator keys 900 into a field scaled 0–999, the block takes it without comment, and the only thing between the jacket and 900 °C is a hardware thermostat nobody has proof-tested since 2019.

PROPERTY: a member that runs code

A PROPERTY looks like a variable from outside and is a pair of small subroutines inside. Writing Jacket.Setpoint := 900.0; calls the SET accessor with 900.0 as its argument. Reading X := Jacket.Setpoint; calls the GET accessor and takes what it hands back. The variable that actually holds the number stays private, and the accessor is the only way past it.

The real thing, in vendor syntax

TryPLC's browser engine executes the IEC 61131-3 2nd-edition core, so it rejects PROPERTY the way it rejects METHOD and EXTENDS. The code above is what you would download to a CODESYS or TwinCAT controller; the live stage on the next screen runs the composition equivalent — the same accessor body, written as ordinary block code — so you can watch it clamp for real.

Push a setpoint past the stops

This is the real ST engine. Key a request into the faceplate and watch the accessor decide what the backing variable is allowed to become. The raw request and the accepted value sit side by side in the online watch.

What does a read actually do?

RuntimeHours is a PROPERTY whose GET divides an internal millisecond counter by 3 600 000. Your program has the line Hrs := Pump.RuntimeHours; and it runs every scan. What happens on that line?

  • Nothing runs — it copies a stored REAL, like any VAR_OUTPUT
  • The GET accessor body executes, then its result is copied into Hrs
  • The GET runs only when the underlying counter has changed
  • The compiler caches the first read for the rest of the scan

Four honest reasons to write one

Validation and clamping on write, so a bad number never reaches the backing variable. Computation on read, so RuntimeHours comes from a millisecond counter that never rounds. Unit conversion at the boundary, so the block stores pulse counts and the plant sees litres. And invariants: a setter can change three things together and leave the block consistent, which a bare public variable can never promise.

The property beside the pin, in both languages

If ladder has to see the value, publish a pin. A property that ladder cannot reach is a property half the plant cannot use.

Price a getter in microseconds

A recipe pass on a 1769-L33ER, MainTask periodic at 20 ms with a 10 ms watchdog. Move the property read in and out of the loop, change what the accessor actually does, and watch the cabinet meter.

The cost nobody sees in the diff

This is the part that is specific to us. A property read compiles to a call, and a call inside a scan-cycle FOR loop is multiplied by the trip count — while the source line looks exactly like reading a variable. Then there is the second surprise: a SET that does something, copied to a new screen by somebody who had every reason to assume an assignment was free.

What did hoisting really change?

A recipe loop runs 500 times per scan and tests Slots[i].Demand > Blender.Setpoint. Moving SP := Blender.Setpoint; above the FOR loop drops the scan from 14 ms to 4 ms. What changed?

  • The loop got shorter
  • 500 accessor calls became 1, and the loop now compares against a local copy
  • The compiler turned the property into a variable
  • Nothing real — that difference is measurement noise

Get-only, set-only

Leave out the SET and the member becomes read-only from outside: the compiler refuses Meter.TotalLitres := 0.0; and your totaliser is something you can defend in an audit. Leave out the GET and you have a write-only member — legal, occasionally used for a command flag, and nearly always better written as a method, because a thing that acts should be named like a verb.

One accessor, not two

RuntimeHours and TotalLitres are derived values: they exist to be read and there is nothing sane to write into them. ClearTotal is the shape to be suspicious of — a write-only member that fires an action is a method that has not admitted it yet.

Property or pin? Decide value by value

Five real values off a dosing skid. Put each one behind the door you would actually build, and read what that choice costs. Nothing resets — move anything, any time.

A computed getter, and the pin that replaces it

A get-only property and a VAR_OUTPUT pin can hold the same number. The property recomputes it when asked; the pin holds a copy that is only as fresh as the last call to the block.

Interfaces can demand properties too

An INTERFACE can require a property, and it requires the accessors with it: declare GET and SET on the interface and every implementing block must supply both, or it does not compile. That is how one generic faceplate can drive a purchased valve block and a home-grown one through the same reference — the interface promises Setpoint is readable and writable, and how each block clamps it stays private.

The setter that moved an axis

You add a SET accessor to Position that clamps the value AND fires MC_MoveAbsolute. A colleague copies the line Axis.Position := 250.0; out of the faceplate code into a diagnostic screen refresh that runs every scan. What happens?

  • Nothing — the accessor only fires when the value changes
  • The axis is re-commanded every scan, because a write is a call and the accessor has a side effect
  • The compiler rejects motion function blocks inside an accessor
  • The move is queued and executed once

What you learned

  • A PROPERTY is a member that looks like a variable and executes an accessor: a read calls GET, a write calls SET.
  • The value of it is the gate — clamp and validate on write, compute and convert on read, and keep the backing variable private.
  • GET-only makes a derived value impossible to corrupt; SET-only is legal and is usually a method wearing the wrong clothes.
  • A property read is a call. One per scan is free; five hundred inside a FOR loop is scan time you pay for at the watchdog.
  • Hoist the read into a local above the loop — and comment that you meant to freeze the value for the pass.
  • Ladder support is a vendor question: CODESYS and TwinCAT can place a property on a rung, Studio 5000 ladder cannot reach one. If ladder must see it, expose a pin.
  • An INTERFACE can demand a property together with its accessors, so an implementer that omits GET does not compile.

PROPERTY — done

  • You can write a property with GET and SET accessors and say exactly when each one runs.
  • You can price a property read in microseconds and keep it out of the loop that would trip the watchdog.
  • You can decide, value by value, whether the plant needs a property, a get-only property, or a plain output pin.