Midterm week: six weeks, one toolkit
420-302-VA · WEEK 7 · FALL 2026

Review 3 of 3 · Weeks 5 and 6 · plan two sessions

Design & classes

Weeks 5 and 6 are one continuous idea: decide what the system must do, design it on paper, prove the design by hand, then give its state and behaviour a home in a class. The exam's richest questions live here, and so does this page's worked micro-example running the entire pipeline on one screen.

The design cycle, end to end

Six stations in a loop: specify requirements, write pseudocode, trace it by hand, diagram the flow or states, implement as code and classes, verify against the spec, and back to specify when verification finds a gap. gaps found? fix the spec 1 · SpecifyR-lines, testable 2 · Pseudocodelogic, no syntax 3 · Traceprove it by hand 4 · Diagramflow and states 5 · Implementcode and classes 6 · Verifytable vs spec
One loop, two weeks. Week 5 taught stations 1 to 4 and 6; Week 6 upgraded station 5 with classes and showed verification doubling as refactoring proof. Exam questions pick a station and ask you to run it, or hand you two stations and ask whether they agree.

Requirements: the five qualities

A requirement says what the system must do, observable from outside; design says how, and the two must not blur. Good R-lines are: testable (a pass/fail check exists), unambiguous (one reading), atomic (one obligation per line), necessary (traceable to a real need) and feasible (buildable with what you have). House style: an ID (R1, R2, ...), condition first, shall for the obligation, a measurable tail, and an acceptance criterion saying how the check is performed. The exam's favourite form is spec repair: given "the system should respond quickly", name the faults (untestable, ambiguous) and rewrite, e.g. "R3: When the button is pressed, the LED shall light within 100 ms, verified by slow-motion video." Repair drill and the full quality definitions: Week 5's requirements page.

Pseudocode: the conventions

Pseudocode is logic without syntax: precise enough to trace, free enough to write at a whiteboard. There is no official standard anywhere, which is why the course fixed one convention set and sticks to it; the exam accepts exactly this style:

ConventionPython it maps to
SET x TO valuex = value
IF condition THEN ... ELSE ... END IF (and ELSE IF)if ... : / elif / else:
WHILE condition ... END WHILE · LOOP FOREVERwhile ... : · while True:
FOR each item IN collection ... END FORfor item in collection:
FUNCTION name(parameters) ... RETURN valuedef name(parameters): ... return value
INPUT ... / OUTPUT ...Reading a sensor or value / print(...) or an actuator
WAIT ... / WAIT UNTIL ...sleep(...) / wait_for_press() and kin

Blocks are shown by indentation plus the explicit END ... line; conversion questions in either direction are mechanical once this table is cold. Refinement is the writing method: start with three fat steps, split each until every line is directly translatable. Full conventions and the construct map: Week 5's pseudocode page.

Trace tables

A trace table executes a program with a pencil: one column per variable (plus condition and output columns), one row per executed line or loop pass, filled strictly in execution order without skipping ahead. The discipline that keeps traces honest: write what the line does, not what you believe the program intends; the gap between the two is where bugs hide, and the exam plants exactly such gaps. Week 6 extended the format by one column: tracing a method call starts by rewriting log.add(431) as add(self=log, ms=431), after which self is just another column. Worked nine-row example: Week 5; method trace: Week 6.

Flowcharts and state machines

Two notations, two questions. A flowchart answers "what happens in what order": ovals for start and end, rectangles for actions, diamonds for decisions with labelled yes/no exits, arrows for flow; every diamond's exits must both lead somewhere, and loops appear as arrows flowing back. A state machine answers "what modes does the system live in": circles for states, arrows for transitions labelled event [guard] / action; the machine is always in exactly one state, and behaviour depends on which. The automation bridge: GRAFCET's steps and transitions are this same formalism in industrial dress, so you already think this way. The Week 6 upgrade made the mapping to code mechanical: state → value of self.mode, event → method call, guard → the if at the method's top, action → the branch's body, extra memory → more attributes. Notation reference: Week 5's flow page; the mapping table: Week 6's state page.

The timing rule

One sentence, frequently examined: wall clock for timestamps, monotonic for intervals. The wall clock (time()) names moments for humans and logs but can jump when the system adjusts it; monotonic() never goes backward, so durations are always monotonic() − t0. Measuring a reaction time with the wall clock is the kind of design flaw spec-repair questions enjoy hiding.

Classes: the compressed core

  • Anatomy. class ReactionLog: opens the blueprint (CapWords name); __init__(self, ...) runs at construction and creates every attribute through self.; methods are defs inside the class with self first. log = ReactionLog("Sam") is a constructor call producing an instance.
  • The self truth. log.add(431) ≡ ReactionLog.add(log, 431): Python passes the instance as the first argument, so inside the method, self is the object the call went through. Listed in every def, passed by no caller.
  • Attribute vs method. State reads without parentheses (led.is_lit); behaviour runs with them (led.on()); a method without parentheses is a value you can store, which is exactly what callbacks do: button.when_pressed = self.press, never self.press().
  • Independence. Each instance owns its state because __init__ creates it fresh per instance; a list written in the class body instead belongs to the blueprint and is shared by all, the classic silent bug.
  • Composition (has-a). Your classes contain gpiozero objects and your own: a NightLight has an LED, has a Button, has a mode. Write has-a; read is-a (the "Bases:" lines in library docs are inheritance, consumed not authored this term).
  • Encapsulation. State changes only through the methods that know its rules, so the mode and the hardware can never drift apart and guards cannot be forgotten by callers.
  • Modules. Any .py file imports by its name from the same folder: from reactionlog import ReactionLog; refactoring means restructuring like this while the old verification table still passes.

Deep versions: First class and State & devices.

One worked micro-example, spec to class

The whole pipeline on a system small enough to memorize: a press counter with a reset rule.

Stations 1 and 2 · Spec, then pseudocode

T1: A new tally shall start at zero. T2: When press() is called, the tally shall increase by one. T3: When the tally reaches 10, the system shall output "FULL" and return to zero. Acceptance: call press() 10 times from new; observe one FULL and a zero count.

FUNCTION press()
    SET count TO count + 1
    IF count = 10 THEN
        OUTPUT "FULL"
        SET count TO 0
    END IF
END FUNCTION

Station 5 · The class

class Tally:
    def __init__(self):
        self.count = 0          # T1

    def press(self):
        self.count = self.count + 1
        if self.count == 10:    # T3
            print("FULL")
            self.count = 0

Stations 3 and 6 are yours: trace eleven calls to press() on a fresh Tally, columns self.count and output. Expected: FULL exactly once, at the tenth call, finishing at count 1. If your table says otherwise, one of us has a bug, and the table will say which line.

Checklist for this review

Check yourself

"The program should use a while loop to check the button often." Which quality does this requirement violate most fundamentally?
It dictates how (a while loop) rather than what: it is design wearing a requirement's clothes, besides being untestable ("often"). A repair states the observable behaviour with a measurable tail, and leaves the loop to the designer.
Convert to Python: WHILE count < 5 ... SET count TO count + 1 ... END WHILE
while count < 5: with count = count + 1 indented beneath. The END WHILE disappears into indentation; the condition and the move toward ending it survive unchanged.
What single discipline makes a trace table an instrument instead of wishful thinking?
Writing what each line does, in strict execution order, never what the program is meant to do. The table is only evidence if it could, in principle, disagree with you.
On a state diagram, where does a transition's guard end up in the class, and what happens when the guard fails?
As the if at the top of the transition's method; on failure the method returns without changing self.mode, so the machine stays put, exactly as the diagram's unfired transition means.
Why is times = [] in the class body a bug for per-instance data, and what is the one-line rule preventing it?
A class-body assignment attaches the object to the blueprint, so every instance sees the same list and "independent" logs merge mysteriously. Rule: every attribute an instance owns is created in __init__, through self.
In the Tally trace, why does the table end at count 1 after eleven presses, not 11?
The tenth press triggers T3: FULL prints and the count resets to zero, so the eleventh press counts from a fresh zero to 1. A trace that shows 11 skipped the reset branch, which is exactly the slip the question exists to catch.