From f195c39db785805411d552b5a100983f9f7e3109 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Fri, 15 Nov 2024 09:26:15 +0100 Subject: [PATCH 1/5] Correctly implemented programs --- wash/src/wash/program/WashingProgram1.java | 32 ++++++-- wash/src/wash/program/WashingProgram2.java | 55 ++++++++++++- wash/src/wash/program/WashingProgram3.java | 17 ++-- wash/src/wash/program/WashingProgramStop.java | 80 ------------------- 4 files changed, 88 insertions(+), 96 deletions(-) delete mode 100644 wash/src/wash/program/WashingProgramStop.java diff --git a/wash/src/wash/program/WashingProgram1.java b/wash/src/wash/program/WashingProgram1.java index 5783210..581ce97 100644 --- a/wash/src/wash/program/WashingProgram1.java +++ b/wash/src/wash/program/WashingProgram1.java @@ -39,19 +39,39 @@ public final class WashingProgram1 extends ActorThread { // Lock the hatch io.lock(true); + // Let water into the machine water.send(new WashingMessage(this, WATER_FILL)); receive(); - // Instruct SpinController to rotate barrel slowly, back and forth - // Expect an acknowledgment in response. - spin.send(new WashingMessage(this, SPIN_SLOW)); + // Heat to 40 C + temp.send(new WashingMessage(this, TEMP_SET_40)); receive(); - // Spin for five simulated minutes (one minute == 60000 milliseconds) + // Keep the temperature for 30 min + Thread.sleep(30 * 60000 / Settings.SPEEDUP); + + // Drain + temp.send(new WashingMessage(this, TEMP_IDLE)); + receive(); + water.send(new WashingMessage(this, WATER_DRAIN)); + receive(); + + // Rinse 5*2 minutes in cold water + for(int a = 0; a < 5; a++) { + water.send(new WashingMessage(this, WATER_FILL)); + receive(); + + Thread.sleep(2 * 60000 / Settings.SPEEDUP); + + water.send(new WashingMessage(this, WATER_DRAIN)); + receive(); + } + + // Centrifuge for five minutes + spin.send(new WashingMessage(this, SPIN_FAST)); + receive(); Thread.sleep(5 * 60000 / Settings.SPEEDUP); - // Instruct SpinController to stop spin barrel spin. - // Expect an acknowledgment in response. spin.send(new WashingMessage(this, SPIN_OFF)); receive(); diff --git a/wash/src/wash/program/WashingProgram2.java b/wash/src/wash/program/WashingProgram2.java index e84e596..860801d 100644 --- a/wash/src/wash/program/WashingProgram2.java +++ b/wash/src/wash/program/WashingProgram2.java @@ -40,9 +40,62 @@ public final class WashingProgram2 extends ActorThread { // Lock the hatch io.lock(true); - // Spin for five simulated minutes (one minute == 60000 milliseconds) + // Let water into the machine + water.send(new WashingMessage(this, WATER_FILL)); + receive(); + + // Heat to 40 C + temp.send(new WashingMessage(this, TEMP_SET_40)); + receive(); + + // Keep the temperature for 20 min + Thread.sleep(20 * 60000 / Settings.SPEEDUP); + + // Drain + temp.send(new WashingMessage(this, TEMP_IDLE)); + receive(); + water.send(new WashingMessage(this, WATER_DRAIN)); + receive(); + + // Let water into the machine + water.send(new WashingMessage(this, WATER_FILL)); + receive(); + // Heat to 60 C + temp.send(new WashingMessage(this, TEMP_SET_60)); + receive(); + + // Keep the temperature for 20 min + Thread.sleep(30 * 60000 / Settings.SPEEDUP); + + // Kill the heat + temp.send(new WashingMessage(this, TEMP_IDLE)); + receive(); + + // Rinse 5*2 minutes in cold water + for(int a = 0; a < 5; a++) { + water.send(new WashingMessage(this, WATER_FILL)); + receive(); + + Thread.sleep(2 * 60000 / Settings.SPEEDUP); + + water.send(new WashingMessage(this, WATER_DRAIN)); + receive(); + } + + // Centrifuge for five minutes + spin.send(new WashingMessage(this, SPIN_FAST)); + receive(); Thread.sleep(5 * 60000 / Settings.SPEEDUP); + spin.send(new WashingMessage(this, SPIN_OFF)); + receive(); + + temp.send(new WashingMessage(this, TEMP_IDLE)); + receive(); + + water.send(new WashingMessage(this, WATER_DRAIN)); + receive(); + // Now that the barrel has stopped, it is safe to open the hatch. io.lock(false); System.out.println("WashingProgram2 Finished..."); diff --git a/wash/src/wash/program/WashingProgram3.java b/wash/src/wash/program/WashingProgram3.java index c7b7f26..79ee344 100644 --- a/wash/src/wash/program/WashingProgram3.java +++ b/wash/src/wash/program/WashingProgram3.java @@ -41,17 +41,16 @@ public final class WashingProgram3 extends ActorThread { io.lock(true); // Switch off heating - // temp.send(new WashingMessage(this, TEMP_IDLE)); - // System.out.println(receive()); - // temp.send(new WashingMessage(this, TEMP_SET_40)); - // System.out.println(receive()); + temp.send(new WashingMessage(this, TEMP_IDLE)); + receive(); - temp.send(new WashingMessage(this, WATER_FILL)); - System.out.println(receive()); - temp.send(new WashingMessage(this, WATER_DRAIN)); - System.out.println(receive()); + // Spin off just to be sure + spin.send(new WashingMessage(this, SPIN_OFF)); + receive(); - // Thread.sleep(5 * 60000 / Settings.SPEEDUP); + // Drain water + water.send(new WashingMessage(this, WATER_DRAIN)); + receive(); // Unlock hatch io.lock(false); diff --git a/wash/src/wash/program/WashingProgramStop.java b/wash/src/wash/program/WashingProgramStop.java deleted file mode 100644 index 410d596..0000000 --- a/wash/src/wash/program/WashingProgramStop.java +++ /dev/null @@ -1,80 +0,0 @@ -package wash.program; - -import actor.ActorThread; -import wash.control.WashingMessage; -import wash.io.WashingIO; - -import static wash.control.WashingMessage.Order.*; - -/** - * Program 3 for washing machine. This also serves as an example of how washing - * programs can be structured. - * - * This short program stops all regulation of temperature and water levels, - * stops the barrel from spinning, and drains the machine of water. - * - * It can be used after an emergency stop (program 0) or a power failure. - */ -public final class WashingProgramStop extends ActorThread { - - private WashingIO io; - private ActorThread temp; - private ActorThread water; - private ActorThread spin; - - public WashingProgramStop(WashingIO io, - ActorThread temp, - ActorThread water, - ActorThread spin) - { - this.io = io; - this.temp = temp; - this.water = water; - this.spin = spin; - } - - @Override - public void run() { - try { - System.out.println("WashingProgramStop Starting..."); - - // Switch off heating - temp.send(new WashingMessage(this, TEMP_IDLE)); - - // Wait for temperature controller to acknowledge - WashingMessage ack1 = receive(); - System.out.println("got " + ack1); - - // Drain barrel, which may take some time. To ensure the barrel - // is drained before we continue, an acknowledgment is required. - water.send(new WashingMessage(this, WATER_DRAIN)); - WashingMessage ack2 = receive(); // wait for acknowledgment - System.out.println("got " + ack2); - - // Now that the barrel is drained, we can turn off water regulation. - water.send(new WashingMessage(this, WATER_IDLE)); - WashingMessage ack3 = receive(); // wait for acknowledgment - System.out.println("got " + ack3); - - // Switch off spin. We expect an acknowledgment, to ensure - // the hatch isn't opened while the barrel is spinning. - spin.send(new WashingMessage(this, SPIN_OFF)); - WashingMessage ack4 = receive(); // wait for acknowledgment - System.out.println("got " + ack4); - - // Unlock hatch - io.lock(false); - - System.out.println("WashingProgramStop Finished..."); - } catch (InterruptedException e) { - System.out.println("WashingProgramStop Interrupted..."); - // If we end up here, it means the program was interrupt()'ed: - // set all controllers to idle - - temp.send(new WashingMessage(this, TEMP_IDLE)); - water.send(new WashingMessage(this, WATER_IDLE)); - spin.send(new WashingMessage(this, SPIN_OFF)); - System.out.println("washing stop program terminated"); - } - } -} From a3167aff1e5de4e6546a2d22a84e55d4e49a7b5a Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Fri, 15 Nov 2024 09:26:58 +0100 Subject: [PATCH 2/5] Interrupt all programs on stop instead of running exit routines --- wash/src/wash/control/Wash.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/wash/src/wash/control/Wash.java b/wash/src/wash/control/Wash.java index 0f0f4bf..5755321 100644 --- a/wash/src/wash/control/Wash.java +++ b/wash/src/wash/control/Wash.java @@ -32,7 +32,7 @@ public class Wash { // if the user presses button 0, and a program has been started, stop it switch (n) { - case 0 -> t = new WashingProgramStop(io, temp, water, spin); + case 0 -> t.interrupt(); case 1 -> t = new WashingProgram1(io, temp, water, spin); case 2 -> t = new WashingProgram2(io, temp, water, spin); case 3 -> t = new WashingProgram3(io, temp, water, spin); @@ -42,7 +42,8 @@ public class Wash { } } - t.start(); + if (!t.isInterrupted()) + t.start(); } temp.interrupt(); From a957490cd811132efefdd62a5a8444bb6a31bf91 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Fri, 15 Nov 2024 09:27:17 +0100 Subject: [PATCH 3/5] Simpler temp and water controllers --- wash/src/wash/control/ControllerTemp.java | 107 +++++++----------- wash/src/wash/control/ControllerWater.java | 125 ++++++++------------- 2 files changed, 88 insertions(+), 144 deletions(-) diff --git a/wash/src/wash/control/ControllerTemp.java b/wash/src/wash/control/ControllerTemp.java index 3caef71..afd0b8b 100644 --- a/wash/src/wash/control/ControllerTemp.java +++ b/wash/src/wash/control/ControllerTemp.java @@ -6,94 +6,67 @@ import wash.control.WashingMessage.Order; public final class ControllerTemp extends ActorThread { private WashingIO io; - private Heater heater; private WashingMessage m; + private WashingMessage temp; + private boolean ackSent = true; + private Order heaterState = Order.TEMP_IDLE; public ControllerTemp(WashingIO io) { this.io = io; - heater = new Heater(0); } protected void sendAck() { m.sendAck(this); + ackSent = true; } @Override public void run() { - heater.start(); - while (true) { try { - m = take(); - } catch (Exception e) { - System.exit(1); - } + temp = receiveWithTimeout(10000 / Settings.SPEEDUP); - switch (m.order()) { - case Order.TEMP_SET_40 -> heater.setTarget(40); - case Order.TEMP_SET_60 -> heater.setTarget(60); - case Order.TEMP_IDLE -> heater.setTarget(0); // TODO: Error - default -> { - continue; + // If there is a new message, swap + if (temp != null) { + m = temp; + ackSent = false; // We have a new order } - } - } - } - /** - * Heater class that extends Thread and controls the temperature - * of the washing machine. - * - * Note that the heater has access to local variables in the - * TemperatureController class. - */ - class Heater extends Thread { - double target; - boolean hovering; + // If this message is not the same as last time + if (m != null && m.order() != heaterState) { + heaterState = m.order(); + } - Heater(double target) { - this.target = target; - this.hovering = true; // No acks before actual setpoint is acquired - } - - synchronized public void setTarget(double target) { - this.target = target; - this.hovering = false; - } - - @Override - public void run() { - double current; - try { - while (!isInterrupted()) { - current = io.getTemperature(); - - if (io.getWaterLevel() == 0) { - io.heat(false); - sleep(60000 / Settings.SPEEDUP); - continue; - } - - if (current < target) { - io.heat(true); - } - - else if (current >= target) { - io.heat(false); - - if (!hovering) { - sendAck(); - hovering = true; - sleep(60000 / Settings.SPEEDUP); - continue; + switch (heaterState) { + case Order.TEMP_SET_40 -> { + if (io.getTemperature() <= 39) { + io.heat(true); + } else { + io.heat(false); + if (!ackSent) + sendAck(); } } - - sleep(60000 / Settings.SPEEDUP); + case Order.TEMP_SET_60 -> { + if (io.getTemperature() <= 59) { + io.heat(true); + } else { + io.heat(false); + if (!ackSent) + sendAck(); + } + } + case Order.TEMP_IDLE -> { + io.heat(false); + if (!ackSent) + sendAck(); + } + default -> { + continue; + } } - } catch (InterruptedException e) { - io.heat(false); - Thread.currentThread().interrupt(); + } catch (Exception e) { + System.out.println("Exception: " + e); } } } diff --git a/wash/src/wash/control/ControllerWater.java b/wash/src/wash/control/ControllerWater.java index 0e7f647..6dcf200 100644 --- a/wash/src/wash/control/ControllerWater.java +++ b/wash/src/wash/control/ControllerWater.java @@ -1,5 +1,7 @@ package wash.control; +import static wash.control.WashingMessage.Order.WATER_IDLE; + import actor.ActorThread; import wash.control.WashingMessage.Order; import wash.io.WashingIO; @@ -8,98 +10,67 @@ public class ControllerWater extends ActorThread { WashingMessage.Order o; WashingMessage m; + WashingMessage temp; WashingIO io; - WaterPid waterpid; + + Order waterState = WATER_IDLE; + boolean ackSent = true; public ControllerWater(WashingIO io) { this.io = io; - this.waterpid = new WaterPid(0, 1); } public void sendAck() { + ackSent = true; m.sendAck(this); } @Override public void run() { - waterpid.start(); while (true) { - // m = poll(60000 / Settings.SPEEDUP).orElse(new WashingMessage(this, Order.NOOP)); try { - m = take(); + temp = receiveWithTimeout(10000 / Settings.SPEEDUP); + + // If there is a new message, swap + if (temp != null) { + m = temp; + ackSent = false; // We have a new order + } + + // If this message is not the same as last time + if (m != null && m.order() != waterState) { + waterState = m.order(); + } + + switch (waterState) { + case Order.WATER_DRAIN -> { + io.drain(true); + io.fill(false); + if (io.getWaterLevel() == 0 && !ackSent) + sendAck(); + } + case Order.WATER_FILL -> { + io.drain(false); + + if (io.getWaterLevel() < 19) + io.fill(true); + else { + io.fill(false); + if (!ackSent) { + sendAck(); + } + } + } + case Order.WATER_IDLE -> { + io.drain(false); + io.fill(false); + } + default -> { + continue; + } + } } catch (Exception e) { - System.exit(1); - } - - switch (m.order()) { - case Order.WATER_DRAIN -> waterpid.setTarget(0); - case Order.WATER_FILL -> waterpid.setTarget(20); - case Order.WATER_IDLE -> waterpid.setIdle(); - default -> { - continue; - } - } - } - } - - /** A pid controller that is not actually a pid controller */ - public final class WaterPid extends Thread { - private double target; - private final double tolerance; // Acceptable range around target - private boolean hover = false; // Indicates if we're close enough to the target - private boolean hasAcknowledged = true; // Tracks if ack has been sent - - public WaterPid(double targetLiter, double tolerance) { - this.target = targetLiter; - this.tolerance = tolerance; - } - - /** Set the target level */ - public synchronized void setTarget(double targetLiter) { - this.target = targetLiter; - this.hover = false; - this.hasAcknowledged = false; // Reset ack when target changes - } - - /** Just coast at current level */ - public synchronized void setIdle() { - this.setTarget(io.getWaterLevel()); - } - - /** Main control loop */ - @Override - public void run() { - while (!isInterrupted()) { - double currentLevel = io.getWaterLevel(); // Get current water level - double error = target - currentLevel; - hover = Math.abs(error) <= tolerance; - - // Open or close drain/fill based on current water level - if (!hover) { - if (error > 0) { - io.fill(true); // Open tap to increase water level - io.drain(false); // Ensure drain is closed - } else { - io.drain(true); // Open drain to decrease water level - io.fill(false); // Ensure tap is closed - } - } else { - // Stop adjustments if hovering near target - io.fill(false); - io.drain(false); - - // Send acknowledgment if target is reached for the first time - if (!hasAcknowledged) { - sendAck(); - hasAcknowledged = true; - } - } - - try { - Thread.sleep(100); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } + System.out.println("Exception: " + e); } } } From 43374804b34e7866a689fdc2dfef77d5af832cc5 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Fri, 15 Nov 2024 09:28:39 +0100 Subject: [PATCH 4/5] Speedup --- wash/src/wash/control/Settings.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wash/src/wash/control/Settings.java b/wash/src/wash/control/Settings.java index 733582c..26433e9 100644 --- a/wash/src/wash/control/Settings.java +++ b/wash/src/wash/control/Settings.java @@ -3,5 +3,5 @@ package wash.control; public interface Settings { // simulation speed-up factor: 50 means the simulation is 50 times faster than // real time. Modify this as you wish. - int SPEEDUP = 50; + int SPEEDUP = 400; } From 1f880a1167998d27e140dec721d9b8dfde87a705 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Fri, 15 Nov 2024 09:28:59 +0100 Subject: [PATCH 5/5] Deprecate take --- wash/src/actor/ActorThread.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wash/src/actor/ActorThread.java b/wash/src/actor/ActorThread.java index bb231a2..18683bb 100644 --- a/wash/src/actor/ActorThread.java +++ b/wash/src/actor/ActorThread.java @@ -25,12 +25,12 @@ public abstract class ActorThread extends Thread { * milliseconds if none available. Returns null if no message is obtained * within 'timeout' milliseconds. */ - @Deprecated protected M receiveWithTimeout(long timeout) throws InterruptedException { return q.poll(timeout, TimeUnit.MILLISECONDS); } /** Wait for a message continuously */ + @Deprecated protected M take() throws InterruptedException { return q.take(); }