Unit 10 · lab
Lab: Reconstruct a Range Failure From Tests, Logs, and Runtime Evidence
This lab treats debugging as an evidence problem.
You will begin with a Java component whose boundary tests already pass, inject one invalid range value into a controlled runtime case, preserve the exception and WPILib-style log evidence, verify the component's modeled recovery state, inspect separate ROS 2 graph/configuration evidence, and write a diagnosis that does not invent a cause.
Your final artifact is a Controlled Diagnosis Record.
Evidence lanes
You do not need physical robot hardware for this lab.
Local WPILib project lane
If you have a local WPILib Java project, use your Week 9 test suite for the JUnit evidence. You may also add the logging calls from this lab to a desktop/simulation project if your environment supports the required WPILib runtime.
Label exactly what you executed.
Reader evidence lane
If you do not have that environment, use the supplied JUnit and WPILib log records in this lab. Label them supplied evidence.
The ROS 2 terminal in the reader remains deterministic and is labeled separately.
Guided example: do not turn a rejection into a diagnosis
Evidence:
raw value: 3.70 m
allowed interval: 0.10..2.00 m
result: IllegalArgumentException
last valid state: 0.42 m
A weak conclusion is:
The sensor is broken.
A defensible conclusion is:
The Java component rejected
3.70 mbecause it exceeded the configured2.00 mmaximum. The object preserved its prior valid state of0.42 m. The source of the invalid input is not established by this evidence.
The second version uses every available fact without inventing a physical cause.
That is the standard for the rest of the lab.
Part 1: Preserve the known-good test baseline
Your Week 9 suite should include the strict caution-boundary case:
0.49 m → stop=true
0.50 m → stop=false
0.51 m → stop=false
and validated sensor cases around:
0.10 m minimum
2.00 m maximum
If you have local test output, preserve it.
Otherwise use this supplied record:
ObstacleGuardTest
stopsImmediatelyBelowBoundary PASSED
continuesExactlyAtBoundary PASSED
continuesImmediatelyAboveBoundary PASSED
ValidatedRangeSensorTest
acceptsExactMinimum PASSED
acceptsExactMaximum PASSED
rejectsReadingBelowMinimum PASSED
rejectsReadingAboveMaximum PASSED
rejectedUpdatePreservesLastValidReading PASSED
What does this baseline establish?
It supports claims about the tested Java code and those supplied cases. It does not establish that the later runtime input source is healthy.
Part 2: Define the runtime logging records
A WPILib application could start logging with:
DataLogManager.start();
For the range case, define named entries such as:
DataLog log = DataLogManager.getLog();
DoubleLogEntry rawRangeLog =
new DoubleLogEntry(
log,
"/sensors/front/rawRangeMeters"
);
DoubleLogEntry acceptedRangeLog =
new DoubleLogEntry(
log,
"/sensors/front/acceptedRangeMeters"
);
BooleanLogEntry healthyLog =
new BooleanLogEntry(
log,
"/sensors/front/healthy"
);
The exact class organization is up to your project. Your diagnostic record must preserve the meaning of each entry.
Part 3: Process a controlled sequence
Use this input series:
double[] runtimeInputs = {
0.42,
0.38,
3.70,
0.41
};
For every input:
- preserve the raw value;
- attempt the validated update;
- if accepted, preserve the new accepted state and mark modeled health
true; - if rejected, preserve the exception context and mark modeled health
false; - do not replace the invalid value with a made-up fallback number.
A simplified implementation might contain:
for (double value : runtimeInputs) {
rawRangeLog.append(value);
try {
sensor.updateRange(value);
acceptedRangeLog.append(
sensor.getRangeMeters()
);
healthyLog.append(true);
DataLogManager.log(
"range accepted"
+ " id=" + sensor.getId()
+ " value=" + value + " m"
);
} catch (IllegalArgumentException error) {
healthyLog.append(false);
DataLogManager.log(
"range rejected"
+ " id=" + sensor.getId()
+ " value=" + value + " m"
+ " reason=" + error.getMessage()
);
}
}
The teaching point is the record design: raw input, accepted state, health state, and failure context remain distinguishable.
Part 4: Use or reconstruct the WPILib log evidence
If you executed the logging locally, use your own results.
Otherwise use this supplied timeline:
| Time | Entry | Value/event |
|---|---|---|
| 42.100 | /sensors/front/rawRangeMeters | 0.42 |
| 42.101 | /sensors/front/acceptedRangeMeters | 0.42 |
| 42.101 | /sensors/front/healthy | true |
| 42.120 | /sensors/front/rawRangeMeters | 0.38 |
| 42.121 | /sensors/front/acceptedRangeMeters | 0.38 |
| 42.121 | /sensors/front/healthy | true |
| 42.140 | /sensors/front/rawRangeMeters | 3.70 |
| 42.141 | messages | range rejected id=range-front value=3.70 m allowed=0.10..2.00 m |
| 42.141 | /sensors/front/healthy | false |
| 42.160 | /sensors/front/rawRangeMeters | 0.41 |
| 42.161 | /sensors/front/acceptedRangeMeters | 0.41 |
| 42.161 | /sensors/front/healthy | true |
Label the table supplied WPILib log evidence if you did not produce it locally.
Part 5: State exactly what happened to component state
Before the invalid input, the last accepted value was:
0.38 m
The 3.70 m request was rejected.
Therefore the component should not adopt 3.70 m as accepted state.
The next accepted value is:
0.41 m
Your record must answer:
- What was the last valid reading before the failure?
- Which raw value was rejected?
- Did the rejected value become accepted state?
- What input returned the modeled health state to
true?
Do not write simply "sensor recovered."
Write what the software state actually did.
Part 6: Inspect independent ROS 2 runtime evidence
The reader terminal is a controlled ROS 2 simulation. It is not connected to your Java or WPILib code.
Inspect a Java application beside a ROS 2 Jazzy graph
Source a simulated Jazzy shell, verify the active distribution, inspect nodes and their graph relationships, and keep runtime observations separate from Java and hardware claims.
This is a controlled command simulator. It does not execute Java, ROS 2, shell commands, or network requests on your device.
echo $ROS_DISTROsource /opt/ros/jazzy/setup.bashecho $ROS_DISTROros2 --helpros2 node listros2 node info /sensor_bridgeros2 node info /motor_guardros2 topic listros2 param get /motor_guard caution_distance_m
Run:
source /opt/ros/jazzy/setup.bash
ros2 node list
ros2 param get /motor_guard caution_distance_m
Record the exact output under a heading:
Controlled ROS 2 runtime evidence
The node list and parameter value are useful observations.
They do not prove that the ROS graph produced the Java runtime input 3.70 m.
Part 7: Add a supplied ROS logging example without merging it into the Java incident
ROS 2 Jazzy supports log severity levels and logger identity. For comparison, use this supplied independent ROS log record:
42.130 WARN sensor_bridge "one range source timeout observed"
Label it:
supplied ROS logging example
Do not write:
The ROS timeout caused the Java range rejection.
The course has not demonstrated that data path.
You may write:
The supplied ROS warning occurs near the Java-side rejection in the comparison timeline, but no causal link has been established.
Part 8: Build the evidence timeline
Create one timeline with a Source column.
Include:
- JUnit baseline;
- raw WPILib inputs;
- accepted WPILib state;
- rejection event;
- modeled health changes;
- controlled ROS graph snapshot;
- ROS parameter observation;
- supplied ROS warning.
If two records do not share a trustworthy clock, do not pretend they do. Mark their timing relationship as unknown or approximate.
Part 9: Write competing hypotheses
Write at least three possible explanations for the 3.70 m input.
Examples include:
- unit conversion error;
- isolated source anomaly;
- configured range mismatch;
- test or injected data;
- incorrect upstream field mapping.
For each hypothesis, name one observation that would make it stronger or weaker.
Do not select a winner without evidence.
Part 10: Choose the next diagnostic action
Pick the single next observation you would collect.
A strong next action answers a specific uncertainty, such as:
inspect the raw source before Java unit conversion
or:
verify whether a documented Java-to-ROS bridge actually feeds this component
A weak next action is:
look at more logs
because it does not say what question the additional record should answer.
Part 11: Write the diagnosis in four sections
Observed
State only facts directly supported by the test, log, and runtime records.
Hypotheses
List plausible explanations still under investigation.
Modeled recovery state
State what the Java/WPILib application did after rejection and what later event changed its modeled state.
Not yet proven
List claims that remain unsupported, including physical sensor failure/recovery and any Java-to-ROS causal connection not demonstrated by architecture evidence.
Success criteria
Your Controlled Diagnosis Record is complete when another student can answer:
- Which software rules were already protected by tests?
- Which runtime value violated the range rule?
- Which record preserved the raw value?
- Which record preserved the rejection context?
- What happened to accepted state after the invalid request?
- What event returned modeled health to
true? - Which evidence came from WPILib and which came from ROS 2?
- Which causal relationship remains unproven?
- What next observation would reduce the uncertainty?
The goal is not to tell the most convincing failure story. It is to tell the strongest story the evidence can actually support.