StateTick Docs Home →
StateTick
How the machine is described
Structure

A name that says where it lives.

Most control projects end up with a long flat list of signals. FILL_V1_OPN, FILL_V1_FB, CAP_TRQ_OK. The structure is real — it lives in the naming convention, in a spreadsheet, and in the head of whoever set it up.

StateTick asks you to build the structure instead of encoding it in a prefix. A signal is not a tag in a list. It is a variable, on an interface, on a piece of equipment, placed in a machine — and its name says exactly that.

01

The vocabulary is built in layers

Each layer answers one question, and only that question.

An interface is a contract: a named set of typed variables. Valve might carry Open, Closed and Command. It says nothing about which valve, or where the valve is wired. It is the shape of a valve.

A piece of equipment is a named thing that implements one or more interfaces. A FillHead might implement Valve and FlowMeter together, because that is what a fill head is: one physical assembly with two aspects.

An instance is that equipment placed inside a particular machine, under a particular name. Filler1, Filler2, Filler3 — or an indexed set, if you have twelve of them and want to talk about Heads[0] through Heads[11].

Put those together and you get the address of a signal:

DEFINED ONCE Interface Valve Open : Bool Command : Int used by Equipment FillHead implements Valve implements FlowMeter placed as Instance Filler1 inside composite “Filling” THE ADDRESS THAT RESULTS Filler1.Valve.Open which one which aspect which signal
Three layers, three questions. Nothing in the name is a convention you have to remember — each segment is a real object you can open.

The variable at the end is properly typed. Bool, Int, Double, String and Time cover the ordinary cases; arrays, keyed maps and mixed-field records cover the ones that would otherwise turn into RECIPE_1_PARAM_07.

Coming from a PLC

This is the same instinct as a UDT and an instance of it — the difference is that the interface, the equipment and the placement are three separate objects you edit in three separate places, and the wiring is a fourth. Change the valve interface and every valve in the plant gains the variable. Rename an instance and every reference to it moves with it.

02

The composite holds the instances

A machine is a container with a boundary, not a region of a shared program.

A composite is one machine. When you open one you are looking at everything that machine has: its state plan, the equipment instances placed in it, its timers, the logic blocks it instantiates, the queues it owns, and how often it runs.

That last one matters more than it sounds. A composite carries its own cycle time. A safety interlock can sit at 2 ms next to a recipe sequencer at 50 ms, on the same controller, because they are separate machines and always were.

Composite — “Filling” cycle 10 ms State plan Idle → Fill → Settle → Done abort and interrupt ladders attached to this plan only Equipment instances Filler1 : FillHead Filler2 : FillHead Scale : LoadCell Timers SettleT DoseT Logic blocks DoseCalc instance of a template Queues OutQ owned by this machine Globals declared and typed, outside every machine Global.BatchId Global.Recipe.Target by name
Everything inside the boundary belongs to this machine. Globals exist, but they are declared, typed, and clearly outside — reaching one is a visible act, not a side effect.

Because the boundary is real, so is the addressing. Inside the composite, Filler1.Valve.Open is unambiguous. Across the project, a global is Global.BatchId and a field of a record global is Global.Recipe.Target. There is no third category of variable that lives nowhere in particular.

03

Write the step once, use it everywhere

A state that says “clamp and wait” should not have to be rewritten for each clamp.

States and conditions are authored in a place of their own, against placeholders rather than against a specific machine. A generic state can start a timer, execute a logic block, set an equipment variable, set a global, or push and pop a queue — but the thing it acts on is left open.

Then, in a composite, you fill the holes. The same template becomes a different concrete state in each machine it is used in, and the mapping is visible: you can see, on one screen, that Target means Filler1 here and Filler2 there.

AUTHORED ONCE State “Clamp” Target.Clamp.Cmd := 1; Timer.Start(Dwell) two placeholders, no machine named yet MAPPED PER MACHINE In “Filling” Target → Filler1 Dwell → SettleT In “Capping” Target → Chuck WHAT RUNS Filler1.Clamp.Cmd := 1; Timer.Start(SettleT) Chuck.Clamp.Cmd := 1; Timer.Start(TorqueT) CHANGE THE TEMPLATE — BOTH MACHINES FOLLOW + Log.Note('clamped') added in one place applies to every machine that uses the template Filling and Capping, both
The template holds the intent; the mapping holds the machine. Editing the intent later reaches every machine that uses it, and the mapping table tells you which ones those are.

Conditions work the same way. A guard can reference an edge on a placeholder signal, a timer, a queue’s state, or another machine’s state — and stays a template until a composite says what those refer to.

The practical effect: a line with eight near-identical stations is authored as one set of states and eight mapping tables, not as eight copies that drift apart over the next two years.

04

The path is already the API

Structure you built for yourself turns out to be the structure everyone else wanted.

Every equipment variable and every global carries a flag: expose this on the OPC UA server. Tick it, and the variable appears in the server’s address space — under a folder tree built by splitting the path you already have.

No export step. No alias table. No second naming scheme that has to be kept in step with the first. The tree a SCADA integrator browses is the machine you drew.

ONE NAME Filler1.Valve.Open Read by the state plan Fill → Settle when Filler1.Valve.Open AND Scale.Weight >= Target Wired by the bus mapping Filler1.Valve.Open → EtherCAT slave 3, bit 4 Browsed over OPC UA StateTick └ Equipment └ Filler1 └ Valve └ Open
Three consumers, one name. The logic reads it, the bus mapping wires it, and the OPC UA server publishes it — and none of the three keeps a private copy of the naming scheme.

Complex variables are handled rather than flattened. An array, a keyed map or a record is expanded into individual nodes, so a client sees the elements it can actually subscribe to rather than one opaque blob.

Globals follow the same rule under their own root, including record fields and indexed elements — Global.Recipe.Target lands where you would look for it.

Worth saying out loud: exposure is per variable, not all-or-nothing. The address space contains what you decided to publish, which is usually a small, deliberate subset — not every internal bit in the controller.

05

Why structure earns its keep

The payoff is not tidiness. It is that questions have answers.

Questions that become easy

  • What does this machine actually have? Open the composite.
  • Where does this signal come from? Follow the path to a mapping row.
  • Who else uses this state? The mapping table lists every machine.
  • What is on OPC UA? The tree, and nothing you did not tick.

Mistakes that stop happening

  • Two signals with the same meaning and different names.
  • A rename that leaves half the references behind.
  • An OPC tag list that no longer matches the controller.
  • A copied station that quietly diverges from its siblings.

You are not naming signals. You are describing a machine.

The flat tag list is not wrong so much as lossy: it throws away the structure of the plant and asks the naming convention to carry it. Build the structure instead and the name becomes a consequence — readable to the person who arrives next year, and precise enough that the tools can follow it.

That is the whole argument for the composer. Interfaces say what a thing is. Equipment says what a thing has. A composite says what a machine is made of. And the address of any signal is just those three answers, joined by dots.

Also worth reading

Sub-Machines and Machine Modes — what happens once you have more than one composite: parallel sections of a machine, and automatic and manual as two plans that hand over cleanly.

Queues: Batches That Flow Between Stages — how one machine hands work to the next when the thing being passed is data, not just timing.