-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathElevatorSystem.java
More file actions
327 lines (273 loc) · 12.4 KB
/
ElevatorSystem.java
File metadata and controls
327 lines (273 loc) · 12.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
import java.util.*;
/**
* ============================================================================
* ULTIMATE ELEVATOR SYSTEM LOW-LEVEL DESIGN (PRODUCTION READY)
* ============================================================================
*/
// ==========================================
// 1. ENUMS & REQUESTS (Strict State Management)
// ==========================================
enum Direction { UP, DOWN, NONE }
enum ElevatorState { IDLE, MOVING, EMERGENCY, MAINTENANCE }
enum DoorState { OPEN, CLOSED }
class Request {
private int floor;
private Direction direction;
public Request(int floor, Direction direction) {
this.floor = floor;
this.direction = direction;
}
public int getFloor() { return floor; }
public Direction getDirection() { return direction; }
}
// ==========================================
// 2. THE ELEVATOR CAR (Hardware & Edge Cases)
// ==========================================
/*
* INTERVIEW EXPLANATION:
* "The Car focuses entirely on hardware limits. It tracks weight, handles the
* physical doors, and overrides its own state if an emergency occurs."
*/
class ElevatorCar {
private int id;
private int currentFloor;
private Direction currentDirection;
private ElevatorState state;
private DoorState doorState;
private final double MAX_WEIGHT_KG = 1000.0;
private double currentWeightKg;
public ElevatorCar(int id) {
this.id = id;
this.currentFloor = 0;
this.currentDirection = Direction.NONE;
this.state = ElevatorState.IDLE;
this.doorState = DoorState.CLOSED;
this.currentWeightKg = 0.0;
}
public int getId() { return id; }
public int getCurrentFloor() { return currentFloor; }
public Direction getCurrentDirection() { return currentDirection; }
public ElevatorState getState() { return state; }
public DoorState getDoorState() { return doorState; }
public void setCurrentFloor(int floor) { this.currentFloor = floor; }
public void setCurrentDirection(Direction dir) { this.currentDirection = dir; }
public void setState(ElevatorState state) { this.state = state; }
// EDGE CASE: Hardware safety lock
public boolean closeDoors() {
if (currentWeightKg > MAX_WEIGHT_KG) {
System.out.println("[CAR " + id + "] ALARM: Weight limit exceeded! Doors staying open.");
return false;
}
this.doorState = DoorState.CLOSED;
System.out.println("[CAR " + id + "] Doors securely closed.");
return true;
}
public void openDoors() {
this.doorState = DoorState.OPEN;
System.out.println("[CAR " + id + "] Doors opened at Floor " + currentFloor);
}
// EDGE CASE: Emergency override
public void triggerEmergencyStop() {
this.state = ElevatorState.EMERGENCY;
this.currentDirection = Direction.NONE;
System.out.println("[CAR " + id + "] EMERGENCY STOP ACTIVATED!");
openDoors();
}
public void updateWeight(double weightDiff) {
this.currentWeightKg += weightDiff;
}
}
// ==========================================
// 3. THE ELEVATOR CONTROLLER (The LOOK Algorithm)
// ==========================================
/*
* INTERVIEW EXPLANATION:
* "The Controller uses two Priority Queues. Min-Heap for going UP, Max-Heap for
* going DOWN. This achieves O(log N) insertion and perfectly sorts the floors
* so the elevator sweeps smoothly without bouncing."
*/
class ElevatorController {
private ElevatorCar car;
private PriorityQueue<Integer> upRequests; // Min-Heap (1, 2, 5, 10)
private PriorityQueue<Integer> downRequests; // Max-Heap (10, 5, 2, 1)
public ElevatorController(ElevatorCar car) {
this.car = car;
this.upRequests = new PriorityQueue<>();
this.downRequests = new PriorityQueue<>(Collections.reverseOrder());
}
public ElevatorCar getCar() { return car; }
// Adds request to the correct heap based on car's current physical location
public void addRequest(int requestedFloor) {
if (requestedFloor > car.getCurrentFloor()) {
if (!upRequests.contains(requestedFloor)) upRequests.offer(requestedFloor);
} else if (requestedFloor < car.getCurrentFloor()) {
if (!downRequests.contains(requestedFloor)) downRequests.offer(requestedFloor);
} else {
System.out.println("[CONTROLLER " + car.getId() + "] Car is already at floor " + requestedFloor);
}
}
// THE ENGINE: Sweeps UP, then sweeps DOWN.
public void runLookAlgorithm() {
while (!upRequests.isEmpty() || !downRequests.isEmpty()) {
// Safety check: Abort if hardware is compromised
if (car.getState() == ElevatorState.EMERGENCY || car.getState() == ElevatorState.MAINTENANCE) {
System.out.println("[CONTROLLER " + car.getId() + "] Halting engine due to critical state.");
return;
}
if (car.getCurrentDirection() == Direction.UP || car.getCurrentDirection() == Direction.NONE) {
processUpRequests();
} else if (car.getCurrentDirection() == Direction.DOWN) {
processDownRequests();
}
}
// Put car to sleep when finished
car.setState(ElevatorState.IDLE);
car.setCurrentDirection(Direction.NONE);
System.out.println("[CONTROLLER " + car.getId() + "] All requests finished. Car is IDLE.");
}
private void processUpRequests() {
car.setCurrentDirection(Direction.UP);
car.setState(ElevatorState.MOVING);
while (!upRequests.isEmpty()) {
int nextFloor = upRequests.poll();
moveCarToFloor(nextFloor);
}
// Sweep finished. If there are people waiting below, flip direction!
if (!downRequests.isEmpty()) {
car.setCurrentDirection(Direction.DOWN);
}
}
private void processDownRequests() {
car.setCurrentDirection(Direction.DOWN);
car.setState(ElevatorState.MOVING);
while (!downRequests.isEmpty()) {
int nextFloor = downRequests.poll();
moveCarToFloor(nextFloor);
}
// Sweep finished. If there are people waiting above, flip direction!
if (!upRequests.isEmpty()) {
car.setCurrentDirection(Direction.UP);
}
}
private void moveCarToFloor(int targetFloor) {
System.out.println("[CONTROLLER " + car.getId() + "] Moving from " + car.getCurrentFloor() + " to " + targetFloor + "...");
car.setCurrentFloor(targetFloor);
System.out.println("[CONTROLLER " + car.getId() + "] Ding! Reached Floor " + targetFloor);
car.openDoors();
// Hardware check before leaving
boolean isSafeToClose = car.closeDoors();
if (!isSafeToClose) {
System.out.println("[CONTROLLER " + car.getId() + "] Waiting for weight to decrease...");
car.updateWeight(-200.0); // Simulating someone stepping off
car.closeDoors();
}
}
}
// ==========================================
// 4. THE DISPATCH STRATEGY (Strategy Pattern)
// ==========================================
interface ElevatorSelectionStrategy {
ElevatorController selectElevator(List<ElevatorController> controllers, Request request);
}
class NearestElevatorStrategy implements ElevatorSelectionStrategy {
@Override
public ElevatorController selectElevator(List<ElevatorController> controllers, Request request) {
ElevatorController bestController = null;
int minDistance = Integer.MAX_VALUE;
for (ElevatorController controller : controllers) {
ElevatorCar car = controller.getCar();
// Ignore broken cars
if (car.getState() == ElevatorState.EMERGENCY || car.getState() == ElevatorState.MAINTENANCE) {
continue;
}
int distance = Math.abs(car.getCurrentFloor() - request.getFloor());
boolean isMovingTowardsUser = false;
// Is it idle, or coming towards us?
if (car.getState() == ElevatorState.IDLE) {
isMovingTowardsUser = true;
} else if (car.getCurrentDirection() == Direction.UP && request.getFloor() >= car.getCurrentFloor()) {
isMovingTowardsUser = true;
} else if (car.getCurrentDirection() == Direction.DOWN && request.getFloor() <= car.getCurrentFloor()) {
isMovingTowardsUser = true;
}
if (isMovingTowardsUser && distance < minDistance) {
minDistance = distance;
bestController = controller;
}
}
// Fallback to first available if all are moving away
if (bestController == null && !controllers.isEmpty()) {
bestController = controllers.get(0);
}
return bestController;
}
}
// ==========================================
// 5. THE GLOBAL DISPATCHER (The Building Manager)
// ==========================================
class ElevatorDispatcher {
private List<ElevatorController> controllers;
private ElevatorSelectionStrategy selectionStrategy;
public ElevatorDispatcher(ElevatorSelectionStrategy selectionStrategy) {
this.controllers = new ArrayList<>();
this.selectionStrategy = selectionStrategy;
}
public void addElevatorController(ElevatorController controller) {
controllers.add(controller);
}
// External hallway button press
public void submitExternalRequest(int floor, Direction direction) {
Request request = new Request(floor, direction);
System.out.println("\n[HALLWAY] Button pressed: Floor " + floor + " going " + direction);
ElevatorController optimalController = selectionStrategy.selectElevator(controllers, request);
if (optimalController != null) {
System.out.println("[DISPATCHER] Routing request to Car " + optimalController.getCar().getId());
optimalController.addRequest(floor);
} else {
System.out.println("[DISPATCHER] CRITICAL ERROR: No available elevators!");
}
}
// Internal car button press (Bypasses strategy!)
public void submitInternalRequest(int carId, int requestedFloor) {
for (ElevatorController controller : controllers) {
if (controller.getCar().getId() == carId) {
System.out.println("\n[INSIDE CAR " + carId + "] Passenger pressed button for Floor " + requestedFloor);
controller.addRequest(requestedFloor);
return;
}
}
}
}
// ==========================================
// 6. MAIN EXECUTION (The Simulation)
// ==========================================
public class ElevatorSystem {
public static void main(String[] args) {
System.out.println("=== STARTING ELEVATOR SYSTEM SIMULATION ===\n");
// 1. Setup the Hardware & Controllers
ElevatorCar car1 = new ElevatorCar(1);
ElevatorController controller1 = new ElevatorController(car1);
ElevatorCar car2 = new ElevatorCar(2);
ElevatorController controller2 = new ElevatorController(car2);
car2.setCurrentFloor(10); // Start car 2 at floor 10
// 2. Setup the Building Dispatcher
ElevatorDispatcher dispatcher = new ElevatorDispatcher(new NearestElevatorStrategy());
dispatcher.addElevatorController(controller1);
dispatcher.addElevatorController(controller2);
// --- SCENARIO 1: Basic Routing ---
// A user on Floor 3 wants to go UP.
// Car 1 is at Floor 0. Car 2 is at Floor 10. Car 1 should be selected!
dispatcher.submitExternalRequest(3, Direction.UP);
// --- SCENARIO 2: The Mid-Flight Reversal (The LOOK Algorithm Test) ---
// The user gets in Car 1 at Floor 3 and presses Floor 8 (UP).
dispatcher.submitInternalRequest(1, 8);
// WHILE Car 1 is preparing to go UP to 8, a troll jumps in and presses Floor 1 (DOWN).
// Because of the Min/Max Heaps, the car will NOT bounce to 1. It will finish
// going up to 8, reverse engines, and THEN go down to 1.
dispatcher.submitInternalRequest(1, 1);
// 3. Start the Engine for Car 1
System.out.println("\n=== FIRING UP ENGINE FOR CAR 1 ===");
controller1.runLookAlgorithm();
System.out.println("\n=== SIMULATION COMPLETE ===");
}
}