15 — ARCHITECT A LINE

An I/O list, a spec and a blank project — design the block structure and prove it runs.

Estimated 26 minutes.

One line, twelve weeks, an empty project

A contract bottler wants line L4: bottles in one end, cased product out the other. Infeed conveyor, a four-head volumetric filler, a capper, a labeller, a reject station, a discharge conveyor, and a CIP cycle for washdown between products. You have thirty-four I/O points, a two-page spec and a blank ControlLogix project. Every screen after this one is a decision somebody will ask you to justify in six months.

The paperwork you actually get

Four clauses are marked because they do not say enough. Real specs are like this — and the gaps are exactly where the architecture gets decided, by you, whether you notice or not.

Find the objects

Read the tag list as nouns. Some need a block type written for them, some are another instance of a type you already have, and some are only data. Sort all nine.

Count types, not devices

Thirty-four I/O points and eleven pieces of equipment collapse into four block types. The test is never how many machines there are — it is how many distinct behaviours there are. Four fill heads behave identically on different pipework, so they are one type and four instances, and the second conveyor is the first conveyor again.

The fill head, in both languages

FillTicks is an input, not a constant. That single decision is what makes clause 6.1 a data change instead of a code change.

The fifth head

The filler is built from one FB_FillHead type and four instances in an array, called by a FOR loop. Operations orders a fifth head. What has to change?

  • FB_FillHead — it has to learn about the fifth head
  • The array bound and the loop limit in the manager
  • A fifth copy of the fill logic, renumbered
  • Nothing — arrays resize themselves at download

Every device gets exactly one owner

A block owns what it declares. The line manager declares the stations, a station declares its devices, and nothing declares its own caller. Break that and you get the two failures every plant has seen: two blocks writing the same output and fighting over it every scan, or a child reaching up to command the sequence that commands it.

Draw the ownership tree

Two carriers, seven slots, one parts bin. Mount each module under the block that should declare it — and try the mounts you think are wrong, because the fault they raise is the lesson.

The tree is just a VAR block

Head 3 closes a scan late and has done for a year. Nobody typed that on purpose — somebody patched one of four copies, which is the only bug the flat version can have.

The pin list is the promise

Once a block has callers, its pins are a contract you cannot quietly break. Everything behind them is yours to rewrite on a Tuesday. So the pins should carry only what the caller genuinely has to know, and every implementation detail — counters, valve profiles, scaling — belongs on the inside where a change costs nothing.

Choose the contract

Seven signals off the fill head's loop sheet. Land each on the input terminals, the output terminals, or inside the casting — then apply the change request and see what your boundary cost.

The convenient output

A colleague adds Ticks to FB_FillHead's VAR_OUTPUT "so the HMI can show fill progress in scans". Six months later you want to close on the flow totaliser instead. What does that cost you now?

  • Nothing — outputs are read-only, so nobody depends on it
  • Every screen, report and historian tag that read Ticks
  • Only a recompile
  • A change to the recipe struct

The manager loop, in both languages

This is the honest comparison. Ladder is not worse here, it is more literal — and the more instances a station has, the more that literalness costs at change time.

Now run the skeleton

Four block types, six instances, one recipe struct and a manager that sequences without actuating. Everything below is the real ST engine executing that program scan by scan — the bottles, the levels, the counter and the reject are the values the engine computed, not an animation of them.

Run the skeleton

Press Run, then scrub. Watch the fill phase get longer when the recipe changes to a litre, watch one slipped cap turn into a reject, and watch CIP take the whole line out of auto without a single station knowing why.

Where the rejected bottle is counted

FB_Capper raises Reject when the torque check fails. Where should the rejected-bottle total be incremented?

  • Inside FB_Capper — it is the block that knows
  • In PRG_Line, which reads Capper.Reject and owns the totals
  • In the reject station block, when the pusher fires
  • In a global counter any block may increment

Twelve months later, the orders arrive

An architecture is not judged on the day it ships. It is judged the first time somebody wants something the spec never mentioned, and the only question that matters is how many files you have to open. Four orders came in this month against line L4.

Route the change requests

Four work orders, six POUs. Decide which block owns each decision, and read what the same order would have cost on the flat version.

The order that does not fit

Quality now wants the line to hold the current batch and alarm if three bottles in the same set are rejected. Nothing owns that rule yet. Where does it go?

  • FB_Capper — it already raises Reject
  • PRG_Line — the batch and the sequence belong to the manager
  • A new FB_QualityMonitor that reads the capper and stops the line
  • The recipe struct, as a per-product limit

What you learned

  • Objects come out of the noun list: count distinct behaviours, not devices — four identical heads are one type and four instances.
  • Ownership points one way. A block owns what it declares, every device has exactly one owner, and nothing commands its own caller.
  • The pin list is the contract; everything behind it is free to change. Publishing an implementation detail freezes it.
  • Product data belongs in a struct the manager owns, so a new product is new values rather than new code.
  • The manager sequences and commands; stations actuate. Totals, modes and batch decisions belong to whoever owns the sequence.
  • Ladder has no loop over an array of instances — the more instances a station has, the more a literal language costs you at change time.
  • An architecture is measured in files-per-change-order, not in elegance on day one.

ARCHITECT A LINE — done

  • You can turn an I/O list and an ambiguous spec into a block structure, and say out loud why each block exists.
  • You can draw an ownership tree that survives a change order, and spot the two mounts that make a program unmaintainable.
  • You have run a composed architecture — an array of function-block instances, a recipe struct and a sequencing manager — on a real ST engine.