Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Your Complete System Model

Over the past nine chapters, you built a coffee machine model piece by piece: structure, ports, connections, actions, states, constraints, requirements, and verification. Each chapter added one concern. This chapter assembles everything into a single coherent system model.

This chapter does not teach new syntax. Instead, it shows how the pieces fit together – and what a complete, reviewable system model looks like.

The Model So Far

Here is what each chapter contributed:

  • Chapter 2 defined the core types: Grinder, Brewer, WaterTank, CoffeeMachine, plus item definitions for Water, CoffeeBeans, and BrewedCoffee.
  • Chapter 5 added typed ports to each component: beanInput and groundOutput on the grinder; waterInlet, groundInput, and brewOutput on the brewer; waterOut on the water tank.
  • Chapter 6 wired the components together with connections and typed item flows.
  • Chapter 7 defined BrewCycle as a sequence of actions with parallel branches.
  • Chapter 8 defined MachineStates with guarded transitions between operating modes.
  • Chapter 9 added BrewTempTarget (calculation) and SafetyInterlock (constraint).
  • Chapter 10 added requirements with satisfy and verify links.

Now we bring all of these into one model.

Package Organization

A complete model needs clear package organization. Here is a practical layout:

package CoffeeMachineDomain {
    import ScalarValues::*;

    // Domain vocabulary
    item def Water;
    item def CoffeeBeans;
    item def BrewedCoffee;

    enum def GrindSetting {
        fine;
        medium;
        coarse;
    }
}
package CoffeeMachineStructure {
    import ScalarValues::*;
    import CoffeeMachineDomain::*;

    port def BeanInputPort {
        in item beans : CoffeeBeans;
    }

    port def GroundCoffeePort {
        out item grounds : CoffeeBeans;
    }

    port def WaterPort {
        in item water : Water;
    }

    port def WaterSupplyPort {
        out item water : Water;
    }

    port def BrewOutputPort {
        out item coffee : BrewedCoffee;
    }

    part def Grinder {
        attribute grindSize : GrindSetting;
        port beanInput : BeanInputPort;
        port groundOutput : GroundCoffeePort;
    }

    part def Brewer {
        attribute waterTemp : Real;
        attribute brewPressure : Real;
        port waterInlet : WaterPort;
        port groundInput : BeanInputPort;
        port brewOutput : BrewOutputPort;
    }

    part def WaterTank {
        attribute currentLevel : Real;
        attribute maxCapacity : Real;
        port waterOut : WaterSupplyPort;
    }

    part def CoffeeMachine {
        part grinder : Grinder;
        part brewer : Brewer;
        part waterTank : WaterTank;
        attribute serialNumber : String;

        connect waterTank.waterOut to brewer.waterInlet;
        connect grinder.groundOutput to brewer.groundInput;
    }
}
package CoffeeMachineBehavior {
    import CoffeeMachineStructure::*;

    action def GrindBeans {
        in item beans : CoffeeBeans;
    }

    action def HeatWater;

    action def ExtractCoffee {
        in item water : Water;
        in item grounds : CoffeeBeans;
        out item coffee : BrewedCoffee;
    }

    action def DispenseCoffee {
        in item coffee : BrewedCoffee;
    }

    action def BrewCycle {
        action grind : GrindBeans;
        action heat : HeatWater;
        action extract : ExtractCoffee;
        action dispense : DispenseCoffee;

        first grind then heat;

        // After heating, extract and dispense in sequence
        first heat then extract;
        first extract then dispense;
    }

    state def Idle;
    state def Brewing;
    state def Cleaning;
    state def Error;

    state def MachineStates {
        state idle : Idle;
        state brewing : Brewing;
        state cleaning : Cleaning;
        state error : Error;

        transition idleToBrewing
            first idle
            then brewing;

        transition brewingToIdle
            first brewing
            then idle;

        transition idleToCleaning
            first idle
            then cleaning;

        transition cleaningToIdle
            first cleaning
            then idle;

        transition anyToError
            first idle
            then error;
    }
}
package CoffeeMachineAnalysis {
    import ScalarValues::*;

    calc def BrewTempTarget {
        in ambient : Real;
        return result : Real;
        result = if ambient < 18.0 ? 94.0 else 93.0;
    }

    constraint def BrewTempSafe {
        in temp : Real;
        temp >= 90.0 and temp <= 96.0;
    }

    constraint def SafetyInterlock {
        in cupDetected : Boolean;
        in brewCommand : Boolean;
        not brewCommand or cupDetected;
    }
}
package CoffeeMachineRequirements {
    import ScalarValues::*;
    import CoffeeMachineStructure::*;
    import CoffeeMachineAnalysis::*;

    requirement def <'REQ-TEMP-001'> BrewTempReq {
        doc /* The extraction temperature shall remain between 90 and 96 degrees Celsius
               during the brew cycle. */
        subject coffeeMachine : CoffeeMachine;
    }

    requirement def <'REQ-SAFE-001'> SafetyInterlockReq {
        doc /* The system shall not begin a brew cycle unless a cup is detected
               on the drip tray. */
        subject coffeeMachine : CoffeeMachine;
    }

    requirement def <'REQ-TIME-001'> BrewTimeReq {
        doc /* A standard brew cycle shall complete within 60 seconds. */
        subject coffeeMachine : CoffeeMachine;
    }
}
package CoffeeMachineVerification {
    import CoffeeMachineRequirements::*;

    verification def BrewTempTest {
        doc /* Measure extraction temperature during a standard brew cycle. */
        objective {
            verify requirement : BrewTempReq;
        }
    }

    verification def SafetyInterlockTest {
        doc /* Attempt to start a brew cycle without a cup. Confirm the system
               refuses. */
        objective {
            verify requirement : SafetyInterlockReq;
        }
    }

    verification def BrewTimeTest {
        doc /* Time a standard brew cycle from start signal to dispensing
               complete. */
        objective {
            verify requirement : BrewTimeReq;
        }
    }
}

How the Packages Connect

The packages form a clear dependency chain:

CoffeeMachineDomain          (items, enums -- no imports)
    ↓
CoffeeMachineStructure       (parts, ports, connections -- imports Domain)
    ↓
CoffeeMachineBehavior        (actions, states -- imports Structure)
    ↓
CoffeeMachineAnalysis        (calcs, constraints -- imports ScalarValues)
    ↓
CoffeeMachineRequirements    (requirements -- imports Structure + Analysis)
    ↓
CoffeeMachineVerification    (verification cases -- imports Requirements)

Each package depends only on packages above it. No circular imports. A reviewer can read the model top-down: vocabulary, then structure, then behavior, then analysis, then requirements, then verification.

Cross-Concern Traceability

The real power of a complete model is traceability across concerns. From any requirement, you can follow links to:

  1. The design element that satisfies it (via subject on the requirement)
  2. The constraint that formalizes its condition (in the Analysis package)
  3. The verification case that will produce evidence (via verify)
  4. The state or action that exercises the relevant behavior

For example, BrewTempReq traces to:

  • CoffeeMachine (the subject that must satisfy it)
  • BrewTempSafe constraint (the formal condition: 90–96 C)
  • BrewTempTest verification case (how you prove compliance)
  • BrewCycle action (the behavior under test)

This is the traceability loop that SysML v2 is designed for. Without it, requirements live in spreadsheets, design lives in diagrams, and verification lives in test plans – all disconnected. With it, everything is linked in one reviewable model.

What Makes This Model Review-Ready

A reviewer looking at this model can answer key questions quickly:

  • What is the system made of? Read CoffeeMachineStructure. Every component, port, and connection is visible.
  • What does it do? Read CoffeeMachineBehavior. The brew cycle sequence and operating modes are explicit.
  • What must it satisfy? Read CoffeeMachineRequirements. Every requirement has a subject and formal text.
  • How will we verify it? Read CoffeeMachineVerification. Every requirement has a planned verification case.
  • What constraints apply? Read CoffeeMachineAnalysis. Safety interlocks and temperature bounds are checkable.

No concern is hidden. No link is implicit. That is the goal.

The Model on Disk

Everything in this book is snippets of one model, and that model exists as files. If you cloned this repository, it is under examples/coffee-machine/ – fifteen .sysml files plus a sysml.toml, checkable with sysml check.

Reading a whole model is a different experience from reading it a construct at a time, and it is worth doing once. Here is which file goes with which chapter:

FileChapter
definitions.sysml3 – definitions and usages
package-structure.sysml4 – packages, visibility, public import
ports-and-interfaces.sysml5, 6 – ports and interface definitions
connections.sysml6 – connections and connectors
flows.sysml6 – flows and messages
actions.sysml7 – actions and control behaviour
brew-cycle-flow.sysml7 – the brew cycle alone, for diagram generation
states.sysml8 – states and transitions
calculations.sysml9 – calculations and constraints
requirements.sysml10 – requirements and verification
typing-and-specialization.sysml12 – typing, specialization, redefinition
metadata.sysml13 – metadata and documentation
views.sysml13 – viewpoints, views, renderings
orchestration.sysml16 – multi-subsystem simulation
demo-analysis.sysml10, 16 – an analysis case

Two other example projects sit beside it: examples/beverage-workspace/, the multi-project workspace Chapter 15 walks through, and examples/views-library/, eleven small models that each isolate one piece of the view chain.

A caveat so you are not surprised: views.sysml reports three diagnostics about satisfying a viewpoint. The model is correct – a viewpoint definition is a kind of requirement definition, so satisfying one is what the specification intends – and the resolver in the current tool does not accept it. The file is left as the specification says it should be written.

Exploring the Model

The complete coffee machine model can be explored from four different perspectives. Each tab shows the same underlying model through a different diagram view:

{{#tab name="Structure" }}
General view (a structural overview, BDD-equivalent): hierarchy with parts, ports, and attributes.
{{#endtab }} {{#tab name="Actions" }}
ActionFlow view: the BrewCycle with fork/join parallelism and decide/merge branching.
{{#endtab }} {{#tab name="States" }}
StateTransition view: machine modes with transitions, guards, and entry/exit actions.
{{#endtab }} {{#tab name="Requirements" }}
General view with a Requirement preset filter: traceability from requirements to design elements.
{{#endtab }} {{#endtabs }}

Extending the Model

This model is a foundation, not a finished product. Real projects extend it in several directions:

  • More requirements. Add power consumption, noise level, maintenance interval requirements.
  • More states. Add Steaming, Descaling, Standby modes with guarded transitions.
  • More actions. Add SteamMilk, RunDescale actions with their own control flow.
  • Variants. Use specialization (Chapter 12) to create EspressoMachine and DripMachine variants.
  • Governance. Use metadata and views (Chapter 13) to add review status and stakeholder-specific model slices.
  • Physical units. Use library quantities (Chapter 14) to replace Real with TemperatureValue and PressureValue.

The structure is ready for all of these. Each extension adds to the model without rewriting what exists.