Chapter 3 — Programming I
Lesson map
Lesson 03 published. Map, example, EX007.
flowchart TD
N0["Learning outcomes"]
N1["3.1 Tags as records"]
N2["3.2 Range validation"]
N3["3.3 Readable output"]
N4["Synthesis"]
N0 --> N1 --> N2 --> N3 --> N4
N4 --> EX["Worked example"]
EX --> Q["Exercise EX007"]
Logical drawing
flowchart LR
N0["Learning outcomes"]
N1["3.1 Tags as records"]
N2["3.2 Range validation"]
N3["3.3 Readable output"]
N4["Synthesis"]
N0 --> N1 --> N2 --> N3 --> N4 --> Q["EX007"]
[Learning outcomes]
|
v
[3.1 Tags as records]
|
v
[3.2 Range validation]
|
v
[3.3 Readable output]
|
v
[Synthesis]
|
v
[Worked example] --> [Exercise EX007]
Learning outcomes
You will (1) read structured data with Python's standard library; (2) validate values against engineering ranges; (3) write output that a reviewer can check line by line; and (4) explain why validation code is a safety-relevant artefact.
3.1 Tags as records
An OT tag list is a table: tag name, engineering unit, low and high limits, current value. Python's csv module reads such tables without third-party packages, which matters on locked-down engineering laptops where installing software needs approval. Treat each row as a dictionary and keep raw text separate from parsed numbers so a parsing failure is visible rather than silently becoming zero.
In the tag lab, keeping the raw text is what lets Maya's note say "FT-301 read −3.0 m³/h" rather than reporting a blank or a zero; the instrument technician can then look at the exact value the meter sent and decide whether scaling or wiring is at fault.
3.2 Range validation
A temperature of 141 °C on a probe calibrated for 20–130 °C is either a real excursion, a failed sensor or a manipulated value. Code cannot tell which; it can flag the row and stop it reaching a report labelled "normal". The same applies to a negative flow, which is physically impossible for a one-way meter and therefore always a data-quality or integrity question.
The tag lab flags exactly these two rows: TT-102 at 141.0 °C against a 130 °C limit, and FT-301 at −3.0 m³/h. Maya sends the first to the operator at once and the second to instrumentation as a suspected wiring or scaling fault.
3.3 Readable output
Print one decision per line with the input that caused it. Reviewers in plant change boards are rarely programmers; they will trust output they can trace to a row.
"""Lab 03: validate a synthetic LDHP tag list (no plant access)."""
import csv, io
RAW = """tag,unit,lo,hi,value
TT-101,degC,20,130,96.5
TT-102,degC,20,130,141.0
PT-201,bar,0,16,9.8
FT-301,m3/h,0,250,-3.0
LT-401,pct,0,100,57
"""
bad = []
for row in csv.DictReader(io.StringIO(RAW)):
v, lo, hi = float(row["value"]), float(row["lo"]), float(row["hi"])
ok = lo <= v <= hi
print(f"{row['tag']:7} {v:7.1f} {row['unit']:5} {'ok' if ok else 'OUT-OF-RANGE'}")
if not ok: bad.append(row["tag"])
print("flagged:", ", ".join(bad))
Run with python3 lab_tags.py. Verified output:
TT-101 96.5 degC ok
TT-102 141.0 degC OUT-OF-RANGE
PT-201 9.8 bar ok
FT-301 -3.0 m3/h OUT-OF-RANGE
LT-401 57.0 pct ok
flagged: TT-102, FT-301
At LDHP, the two flagged rows print as TT-102 141.0 degC OUT-OF-RANGE and FT-301 -3.0 m3/h OUT-OF-RANGE, with the input value on the same line as the decision; routing each flag to the operator or to instrumentation is Maya's judgement in her note, not something the script decides. A change-board reviewer who is a control engineer, not a programmer, can check each line against the instrument index in a minute and does not need to read the script. If a line cannot be traced to a row, the reviewer has no reason to trust it, so the output should never print a summary count without the rows behind it.
Worked example (synthetic LDHP)
The lab flags TT-102 (141.0 °C, above 130) and FT-301 (−3.0 m³/h, below 0). Maya's note separates them: TT-102 could be a genuine high reading and goes to the operator immediately; FT-301 is physically impossible and goes to instrumentation as a suspected wiring or scaling fault, with integrity review if the fault cannot be explained.
Synthetic case — Lakeside District Heating Plant. The tag list in the lab is invented; real LDHP limits would come from the instrument index maintained by the control engineers.
Errors and limits
Range checks catch gross errors, not subtle manipulation inside the band. Floating-point comparison at the boundary needs a stated rule (inclusive here). The script reads a string, not a live system.
Exercises
- EX007 — Extending the tag table. Add row
TT-103,degC,20,130,130.0. What does the lab print for it and why? Rubric: 3 = ok, because limits are inclusive; 2 = ok without reason; 1 = says out-of-range; 0 = none. - EX008 — Unparseable values. Describe what the script does if a value cell contains
n/a, and propose a fix. Rubric: 3 = ValueError stops the run; catch it and flag BAD-VALUE; 2 = one of the two; 1 = says it becomes zero; 0 = none. - EX009 — Triage of two flags. Explain why TT-102 and FT-301 go to different people. Rubric: 3 = plausible excursion vs impossible value, operator vs instrumentation; 2 = partial; 1 = treats both alike; 0 = none.
Synthesis
Small, readable stdlib programs that flag rather than fix are the first tool an OT security student should trust and be able to defend.
Continue studying — chapter 03 of 40
← Chapter 02 (draft) · Course chapter index · Chapter 04 (draft) →