Sensor Fleet Registry and Event Summary

EE1021 Programming for Engineers · Lecture 5 Lab · 2026–2027

Basri Erdogan

Lab Outcomes

By the end of the lab, you should be able to:

  1. Build and update a sensor registry with a dictionary
  2. Retrieve required and optional keys appropriately
  3. Iterate over dictionary values and key–value pairs
  4. Build a frequency dictionary using get
  5. Use sets to compare registered, calibrated, active, and faulty sensors
  6. Explain one dictionary or set operation during an individual checkoff

Working Method

For every task:

  1. Predict the state or output before running.
  2. Implement one small step.
  3. Run and compare actual with predicted behavior.
  4. Explain one correction in a comment.
  5. Rotate roles before the next stage.

Open starter/lecture5_lab.py and keep the supplied names unchanged.

Pre-Lab Diagnostic

readings = {"S1": 4.92, "S2": 5.08}
readings["S2"] = 5.10
readings["S3"] = 4.97
print(len(readings), readings["S2"])

Predict:

  1. Does the S2 assignment add or replace?
  2. What is printed?
  3. What would readings[“S9”] do?

Check: S2 is replaced; 3 5.1 is printed; a missing direct lookup raises KeyError.

Stage 1: Build the Registry

Task 1: Create and Inspect

Create:

readings = {"S1": 4.92, "S2": 5.31, "S3": 4.97}

Print:

  • the S1 reading;
  • the number of registered sensors;
  • whether S4 is registered;
  • a list of all sensor IDs.

Record predictions before running.

Task 2: Add, Update, and Remove

Perform these operations in order:

  1. Add S4 with reading 5.02.
  2. Correct S2 to 5.08.
  3. Remove S3 with pop and store its old reading.
  4. Attempt to remove S9 safely with a default.

Print the dictionary after each operation and print both returned values.

Check-Off A: Registry Semantics

Show your partner, then the instructor if requested:

  • one assignment that adds a key;
  • one assignment that replaces a value;
  • one membership test;
  • one safe lookup using get;
  • one sentence explaining what dictionary membership tests.

Stage 2: Iterate and Count

Task 3: Summarize Readings

Complete:

def average_reading(readings):
    """Return the average of a non-empty reading dictionary."""
    total = 0
    for value in readings.values():
        total += value
    return total / len(readings)

Test with {“A”: 1.0, “B”: 2.0, “C”: 3.0}. The expected result is 2.0.

Task 4: Classify Every Sensor

Implement:

def voltage_status(value, low=4.75, high=5.25):
    """Return low, normal, or high."""
    ...

Then iterate over readings.items() and build:

statuses = {"S1": "normal"}  # continue for every sensor

Do not request input inside either function.

Task 5: Count the Statuses

Build a second dictionary mapping status names to counts.

status_counts = {}
for status in statuses.values():
    status_counts[status] = status_counts.get(status, 0) + 1

Print the result and verify that the sum of its values equals the number of sensors.

Counting Checkpoint

counts = {}
for event in ["alarm", "normal", "alarm"]:
    counts[event] = counts.get(event, 0) + 1

After each iteration, record the complete dictionary state.

Check: {“alarm”: 1}; then {“alarm”: 1, “normal”: 1}; finally {“alarm”: 2, “normal”: 1}.

Stage 3: Compare Sensor Groups

Task 6: Create the Sets

registered = set(readings)
active = {"S1", "S2", "S4", "S5"}
calibrated = {"S1", "S4"}
faulty = {"S2", "S5"}

Explain why set(readings) contains sensor IDs rather than reading values.

Confirm that empty braces create a dictionary and set() creates an empty set.

Task 7: Answer Relationship Questions

Write one set expression for each:

  1. registered and active;
  2. active but not calibrated;
  3. registered sensors marked faulty;
  4. every ID appearing in registered or active;
  5. IDs appearing in exactly one of registered and active.

Print each result with a clear label.

Check-Off B: Set Reasoning

Draw or explain the result of one instructor-selected expression:

  • registered & active
  • active - calibrated
  • registered & faulty
  • registered | active
  • registered ^ active

State what the result means in words before running it.

Stage 4: Integrate the Report

Task 8: Build a Fleet Report

Implement:

def fleet_report(readings, calibrated):
    """Return a dictionary containing summary information."""
    ...

Return a dictionary containing:

  • sensor_count;
  • average_reading;
  • status_counts;
  • needs_calibration.

Use your earlier functions instead of repeating their logic.

Optional Extension: Events from a File

The starter folder includes sensor_events.txt.

with open("sensor_events.txt", encoding="utf-8") as file:
    events = file.read().split()

event_counts = {}
for event in events:
    event_counts[event] = event_counts.get(event, 0) + 1

Move the Python file and text file together before running this extension.

Final Workflow

Your completed program should:

  1. maintain one sensor-reading dictionary;
  2. retrieve optional values without crashing;
  3. build a sensor-status dictionary;
  4. build status counts with get;
  5. compare sensor groups with sets;
  6. return one integrated report dictionary;
  7. pass all supplied assertions.

Individual Checkoff

Each student completes one randomly selected action without partner help:

  • safely look up a missing sensor;
  • add versus update an entry and explain the difference;
  • write one items() loop;
  • repair a frequency-count statement;
  • calculate and interpret one set relationship;
  • explain why empty braces do not create an empty set.

See checkoff_rubric.qmd for the criteria.

Exit Ticket

Submit three short responses:

  1. A safe lookup for S9 with default 0.0.
  2. One loop that receives both sensor ID and reading.
  3. The set expression for active sensors that need calibration.