| Lesson 8 | Course project, part 2 |
| Objective | Write classes for traffic signals |
RED → GREEN → YELLOW → RED in order; minimum time per state; typically no maximum on GREEN.WALK → FLASHING_DONT_WALK → DONT_WALK → WALK with a minimum time per state.DONT_WALK while cars stay GREEN.ScheduledExecutorService to schedule timed transitions; keep one thread for determinism in the course project.RED (stops cars) → GREEN (flows) → YELLOW (clearance) → RED.DONT_WALK (default) → WALK (upon approved request) → FLASHING_DONT_WALK (clear crosswalk) → DONT_WALK.Enums and timing
import java.time.Instant;
import java.util.concurrent.*;
enum CarState { RED, GREEN, YELLOW }
enum PedState { DONT_WALK, WALK, FLASHING_DONT_WALK }
final class Timing {
// Seconds (tune for your simulator / tests)
static final int CAR_MIN_GREEN = 20;
static final int CAR_YELLOW = 4;
static final int PED_WALK_MIN = 7;
static final int PED_FLASH_MIN = 5;
}
Car traffic light
final class CarLight {
private CarState state = CarState.GREEN;
private Instant since = Instant.now();
CarState state() { return state; }
long ageSeconds() { return java.time.Duration.between(since, Instant.now()).getSeconds(); }
void toRed() { state = CarState.RED; since = Instant.now(); }
void toGreen() { state = CarState.GREEN; since = Instant.now(); }
void toYellow() { state = CarState.YELLOW; since = Instant.now(); }
boolean minGreenSatisfied() { return state == CarState.GREEN && ageSeconds() >= Timing.CAR_MIN_GREEN; }
}
Pedestrian signal
final class PedSignal {
private PedState state = PedState.DONT_WALK;
private Instant since = Instant.now();
private volatile boolean pendingRequest = false;
PedState state() { return state; }
long ageSeconds() { return java.time.Duration.between(since, Instant.now()).getSeconds(); }
void pressButton() { pendingRequest = true; }
boolean hasRequest() { return pendingRequest; }
void clearRequest() { pendingRequest = false; }
void toDontWalk() { state = PedState.DONT_WALK; since = Instant.now(); }
void toWalk() { state = PedState.WALK; since = Instant.now(); }
void toFlashingDontWalk() { state = PedState.FLASHING_DONT_WALK; since = Instant.now(); }
boolean minWalkSatisfied() { return state == PedState.WALK && ageSeconds() >= Timing.PED_WALK_MIN; }
boolean minFlashSatisfied() { return state == PedState.FLASHING_DONT_WALK && ageSeconds() >= Timing.PED_FLASH_MIN; }
}
Coordinator - the safety brain
final class IntersectionCoordinator implements AutoCloseable {
private final ScheduledExecutorService sched = Executors.newSingleThreadScheduledExecutor();
private final CarLight car;
private final PedSignal ped;
IntersectionCoordinator(CarLight car, PedSignal ped) {
this.car = car; this.ped = ped;
validateInvariant();
scheduleIdleLoop();
}
// Public API: simulate a push button
void requestCrossing() { ped.pressButton(); }
// Idle: if car is GREEN with no request, stay green; periodically check for requests
private void scheduleIdleLoop() {
sched.scheduleAtFixedRate(this::tick, 0, 500, java.util.concurrent.TimeUnit.MILLISECONDS);
}
private void tick() {
try {
// If a request exists and car green min time is satisfied, start ped phase
if (ped.hasRequest() && car.state() == CarState.GREEN && car.minGreenSatisfied()) {
beginPedestrianPhase();
}
// If in the middle of a phase, drive it forward
advanceIfNeeded();
validateInvariant();
} catch (Exception ex) {
ex.printStackTrace(); // for course project; replace with logging in production
}
}
private void beginPedestrianPhase() {
ped.clearRequest();
// Car: GREEN → YELLOW (fixed duration), then → RED
if (car.state() == CarState.GREEN) car.toYellow();
sched.schedule(() -> {
if (car.state() == CarState.YELLOW) car.toRed();
// Ped: DONT_WALK → WALK
if (ped.state() == PedState.DONT_WALK) ped.toWalk();
validateInvariant();
}, Timing.CAR_YELLOW, TimeUnit.SECONDS);
}
private void advanceIfNeeded() {
// Advance pedestrian cycle
if (ped.state() == PedState.WALK && ped.minWalkSatisfied()) {
ped.toFlashingDontWalk();
} else if (ped.state() == PedState.FLASHING_DONT_WALK && ped.minFlashSatisfied()) {
ped.toDontWalk();
// Return car to GREEN when pedestrians have cleared
if (car.state() == CarState.RED) car.toGreen();
}
}
// Safety: whenever car is GREEN, ped must be DONT_WALK
private void validateInvariant() {
if (car.state() == CarState.GREEN && ped.state() != PedState.DONT_WALK) {
throw new IllegalStateException("Safety invariant violated: GREEN requires DONT_WALK.");
}
}
@Override public void close() { sched.shutdownNow(); }
}
Demo harness
public final class TrafficSimulation {
public static void main(String[] args) throws Exception {
CarLight car = new CarLight();
PedSignal ped = new PedSignal();
try (IntersectionCoordinator ctl = new IntersectionCoordinator(car, ped)) {
// Simulate a pedestrian request after some time:
TimeUnit.SECONDS.sleep(5);
ctl.requestCrossing();
// Let the simulation run long enough to cycle:
TimeUnit.SECONDS.sleep(Timing.CAR_MIN_GREEN + Timing.CAR_YELLOW + Timing.PED_WALK_MIN + Timing.PED_FLASH_MIN + 5);
}
}
}
Instant.now() and scheduler behind small interfaces to inject a fake clock in unit tests.