Compare commits
	
		
			5 commits
		
	
	
		
			819da6a4c3
			...
			1f880a1167
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 1f880a1167 | ||
|   | 43374804b3 | ||
|   | a957490cd8 | ||
|   | a3167aff1e | ||
|   | f195c39db7 | 
					 9 changed files with 181 additions and 244 deletions
				
			
		|  | @ -25,12 +25,12 @@ public abstract class ActorThread<M> 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(); | ||||
|     } | ||||
|  |  | |||
|  | @ -6,94 +6,67 @@ import wash.control.WashingMessage.Order; | |||
| 
 | ||||
| public final class ControllerTemp extends ActorThread<WashingMessage> { | ||||
|     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); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  |  | |||
|  | @ -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> { | |||
| 
 | ||||
|     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); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  |  | |||
|  | @ -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; | ||||
| } | ||||
|  |  | |||
|  | @ -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(); | ||||
|  |  | |||
|  | @ -39,19 +39,39 @@ public final class WashingProgram1 extends ActorThread<WashingMessage> { | |||
|             // 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(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -40,9 +40,62 @@ public final class WashingProgram2 extends ActorThread<WashingMessage> { | |||
|             // 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..."); | ||||
|  |  | |||
|  | @ -41,17 +41,16 @@ public final class WashingProgram3 extends ActorThread<WashingMessage> { | |||
|             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); | ||||
|  |  | |||
|  | @ -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<WashingMessage> { | ||||
| 
 | ||||
|     private WashingIO io; | ||||
|     private ActorThread<WashingMessage> temp; | ||||
|     private ActorThread<WashingMessage> water; | ||||
|     private ActorThread<WashingMessage> spin; | ||||
|      | ||||
|     public WashingProgramStop(WashingIO io, | ||||
|                            ActorThread<WashingMessage> temp, | ||||
|                            ActorThread<WashingMessage> water, | ||||
|                            ActorThread<WashingMessage> 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"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue