SpeQTr Cookbook

This document provides a set of specifications that cover some common features and use cases of the SpeQTr language. You can use these as starting points for constructing your own specifications. Since queries are basically event patterns you can also use the event patterns in these example specifications as inspiration for writing your own queries.

# Check for Specific Failure Event

This specification checks that a specific failure event never occurs.

behavior "No failures"
    prohibited case "Catastrophic failure"
        catastrophic_failure@commander
    end
end

# No Critical Failures

This specification checks that there are no events with severity higher than level 6, which we consider to be critical failures. You could write similar specifications using any other event or timeline attributes.

behavior "No high severity events"
    prohibited case "Severity above 6"
        *@*(_.severity > 6)
    end
end

# Metadata

This example shows how you can add metadata attributes to various levels of a specification. The metadata key names used here are just examples - you can use whatever keys you want to fit your process or application.

# @author = "phil"
# @project = "documentation"

# @type = "example"
behavior "Example behavior"
    when "A message is sent"
        sent_message@worker AS sent
    end

    # @requirement_id = 3
    nominal case "The message is received"
        sent FOLLOWED BY received_message@monitor
    end

    # @requirement_id = 7
    # @category = "remediation"
    recovery case "Message retry"
        sent FOLLOWED BY retry_message@worker FOLLOWED BY received_message@monitor
    end

    # @requirement_id = 9
    # @category = "failure"
    prohibited case "Connection lost"
        connection_lost@monitor
    end
end

# Event attribute stays within bounds

This specification checks that a certain event's attribute was never outside the appropriate bounds.

behavior "Voltage within bounds"
    prohibited case "Voltage is outside permitted range"
        filtered_voltage@battery(_.voltage < 11.5 OR _.voltage > 12.3)
    end
end

# Event attribute stays above threshold

This specification is similar in structure to the one above except that it only checks an upper bound. The other notable aspect here is the fact that, for this specification, we have instrumented our simulator, allowing us to check values that it reports as well.

behavior "Maximum velocity"
    prohibited case "Velocity above maximum bound"
        ground_truth_velocity@simulator(_.velocity > 300)
    end
end

# Check Pattern Across Subsystems

This specification checks that a certain pattern always occurs as expected, specifically that every time the land detection subsystem detects takeoff the commander subsystem registers it. We have bounded the case evaluation by requiring it to occur before any subsequent takeoff_detected event.

behavior "Takeoff detection"
    when "Land detector takeoff"
        takeoff_detected@land_detector as LandDetectorTakeoff
    end

    until "Subsequent takeoff event"
        takeoff_detected@land_detector
    end

    nominal case "Commander takeoff detected"
        LandDetectorTakeoff FOLLOWED BY takeoff@commander
    end
end

# Check Time-Sensitive Pattern

This specification checks that the system's pre-flight checks complete successfully within the required amount of time. It verifies both the timing aspect and that the checks were successful (with the event attributes). See the note in the section on time-limited relationships for requirements for time-based specifications.

behavior "Preflight checks"
    when "Preflight checks begin"
        sensors_preflight_check@commander(_.action = "begin") AS CheckSensors
    end

    nominal case "Checks pass within 100ms"
        CheckSensors FOLLOWED BY WITHIN 100ms preflight_check_complete@commander(_.success = true)
    end
end

# Check Time-Sensitive Multi-Event Pattern

This specification checks that gyroscope-based failure detection always proceeds as expected. Specifically, every gyroscope reading should be reported within 100 milliseconds, and a multi-timeline sequence of events ending at the failure detector should be observed. This example demonstrates the use of recovery case to specify correct remediation behavior alongside nominal behavior.

behavior "Gyroscope informs failure detection"
    when "A gyroscope reading is taken"
        *@gyroscope(_.name = "x_axis" OR _.name = "y_axis") AS GyroscopeReading
    end

    until "Next gyroscope reading is taken"
        *@gyroscope(_.name = "x_axis" OR _.name = "y_axis")
    end

    nominal case "Gyroscope reading flows to failure detector within 100ms"
        GyroscopeReading FOLLOWED BY
        sample_reported@gyroscope AS GyroscopeReport FOLLOWED BY
        sensors_polled@sensors FOLLOWED BY
        failure_detector_updated@commander as Detected
        AND GyroscopeReading FOLLOWED BY WITHIN 100ms Detected
    end

    recovery case "Gyroscope report requires a retry"
        GyroscopeReport FOLLOWED BY
        sample_report_retry@gyroscope FOLLOWED BY
        sensors_polled@sensors FOLLOWED BY
        failure_detector_updated@commander
    end
end

# Aggregate in a Specification

This specification checks some statistical properties of the given event's attributes.

behavior "Gyroscope x axis distribution"
    nominal case "Gyroscope x axis mean and stddev within tolerance"
        x_axis@gyroscope AS x
        AGGREGATE
            -10.0 < mean(x.acceleration) < 10.0
            AND std_dev(x.acceleration) < 2.1
    end
end

# Negative Lookahead

This specification shows another way to write the check pattern across subsystems example above, using the negative lookahead operator. Where the example above says that a certain event should always be followed by another one, this specification says that we should never see the first event without then seeing the second event.

behavior "Takeoff detection"
    prohibited case "Commander takeoff not detected"
        takeoff_detected@land_detector NOT FOLLOWED BY takeoff@commander
    end
end

# Check Absence of Event On Path

This specification checks that there is a path from mission startup to mission end that does not have any events with the "failure" tag on it. We use a negative path constraint to express this.

behavior "Full mission without failure"
    when "Mission starts"
        mission_start@commander AS MissionStart
    end

    nominal case "Mission ends without any failures occurring"
        MissionStart NOT FOLLOWED BY *@*(_.tag = "failure") FOLLOWED BY mission_end@commander
    end
end

# Check Events Have Same Attribute Value

This specification checks that every velocity reading is acknowledged by the commander subsystem with an event that has the same attribute value.

behavior "Velocity readings acknowledgment"
    when "A velocity reading is taken by the sensor"
        velocity_reading@sensors AS SensorVelocity
    end

    nominal case "The commander receives the correct velocity reading"
        SensorVelocity FOLLOWED BY velocity_received@commander AS CommanderVelocity AND
        SensorVelocity.velocity = CommanderVelocity.velocity
    end
end

# System-of-Systems

It is useful to split up your behaviors based on the scope at which they apply. Some describe the local behavior of a single component (on one timeline). Others talk about components working together in a subsystem, or those subsystems communicating to a complete system. You can even go as far as multiple systems working together, often with a coordinator, as a system-of-systems.

For specification purposes, it is best to consider these boundaries as porous. Even when describing system-of-systems level behavior, you can (and should) describe about how the behavior of a single component should contribute to the overall goal.

behavior "Local behavior: Stepper motor actuator"
    when "An actuation request is received"
      actuate_request@motor_control as Req
    end

    nominal case "Actuation is performed"
      Req
        FOLLOWED BY WITHIN 2ms
      actuate@motor_control(_.id = Req.id)
    end
end


behavior "Subystem behavior: Motor actuation is measured"
    when "An actuation occurs within a subsystem control cycle"
      cycle_start@drive_train_subsystem as start
        FOLLOWED BY
      actuate_request@drive_train_subsystem
        FOLLOWED BY
      actuate_request@motor_control as Req
    end
    until "The next control cycle"
      cycle_start@drive_train_subsystem
    end

    nominal case "Rotation is measured in allowed range (+/- 10%)"
      start -> measure@tachometer as tach
      AND tach.rpm >= Req.rpm * 0.9
      AND tach.rpm <= Req.rpm * 1.1
    end
end


behavior "System behavior: waypoint receipt should cause the robot to move"
    when "An actuation occurs within a subsystem control cycle"
      recv_waypoint@gateway as waypoint
    end

    nominal case "Rotation is measured in allowed range (+/- 10%)"
      waypoint
        FOLLOWED BY WITHIN 10 s
      actuate_request@drive_train_subsystem
    end
end


behavior "System-of-Systems behavior: if waypoint receipt doesn't work, the control center should hear about it"
    when "An actuation occurs within a subsystem control cycle"
      recv_waypoint@gateway as waypoint
    end

    nominal case "Rotation is measured in allowed range (+/- 10%)"
      waypoint
        FOLLOWED BY WITHIN 10s
      actuate_request@drive_train_subsystem
    end

    recovery case "Error message reported to control center"
      waypoint
        FOLLOWED BY
      incident_report@control_center(_.vehicle_id = waypoint.vehicle_id)
    end
end