001 package dk.deepthought.sidious.greenhouse;
002 
003 import java.util.ArrayList;
004 import java.util.Collection;
005 
006 import net.jcip.annotations.Immutable;
007 
008 import org.apache.commons.logging.Log;
009 import org.apache.commons.logging.LogFactory;
010 
011 import dk.deepthought.sidious.supportsystem.Adjustable;
012 import dk.deepthought.sidious.supportsystem.State;
013 import dk.deepthought.sidious.supportsystem.SuperLinkID;
014 import dk.deepthought.sidious.supportsystem.SystemSettings;
015 
016 /**
017  * This class represents the setpoint for the greenhouse windows.
018  
019  @author Deepthought
020  
021  */
022 @Immutable
023 public class WindowSetPoint implements Adjustable {
024     /**
025      * Logger for this class
026      */
027     private static final Log logger = LogFactory.getLog(WindowSetPoint.class);
028 
029     /**
030      * The setting of this setpoint.
031      */
032     private final double setting;
033 
034     /**
035      * The id of the temperature sensor.
036      */
037     private final SuperLinkID temperatureID;
038 
039     /**
040      * The id of the humidity sensor.
041      */
042     private final SuperLinkID humidityID;
043 
044     /**
045      * The id of the outside temperature sensor.
046      */
047     private final SuperLinkID outsideTemperatureID;
048 
049     /**
050      * The id of the outside humidity sensor.
051      */
052     private final SuperLinkID outsideHumidityID;
053 
054     /**
055      * Approximate window surface percentage.
056      */
057     private static final int windowAreaPercentage = 10;
058 
059     /**
060      * Internal enum to describe possible adjustments.
061      */
062     private enum WindowStep {
063         OPEN(5d), CLOSE(-5d);
064 
065         private double increment;
066 
067         WindowStep(double increment) {
068             this.increment = increment;
069         }
070 
071         public double getIncrement() {
072             return increment;
073         }
074     }
075 
076     /**
077      * Creates a new <code>ScreenSetPoint</code> with the specified setting.
078      <p>
079      * Input parameters should be between 0 and 100 percent. Parameters outside
080      * this will be rounded to nearest limit.
081      
082      @param setting
083      *            the setting
084      */
085     public WindowSetPoint(double setting) {
086         humidityID = SystemSettings.getHumidityID();
087         temperatureID = SystemSettings.getTemperatureID();
088         outsideHumidityID = SystemSettings.getOutsideHumidityID();
089         outsideTemperatureID = SystemSettings.getOutsideTemperatureID();
090         if (setting > 100) {
091             setting = 100;
092         }
093         if (setting < 0) {
094             setting = 0;
095         }
096         this.setting = setting;
097     }
098 
099     /*
100      * (non-Javadoc)
101      
102      * @see dk.deepthought.sidious.supportsystem.Adjustable#consequence(dk.deepthought.sidious.supportsystem.State)
103      */
104     public State consequence(State state) {
105         if (logger.isDebugEnabled()) {
106             logger.debug("consequence(State state=" + state + ") - start");
107         }
108         if (!(state instanceof ClimaticState)) {
109             String fail = "Input state must be a climatic state. - state=" + state;
110             logger.error(fail);
111             throw new IllegalArgumentException(fail);
112         }
113         ClimaticState clima = (ClimaticStatestate;
114         Collection<SensorInput> sensors = clima.getSensors();
115         if (sensors.isEmpty()) {
116             String fail = "Sensors cannot be empty";
117             logger.error(fail);
118             throw new IllegalArgumentException(fail);
119         }
120         ArrayList<SensorInput> newSensorList = new ArrayList<SensorInput>();
121         SensorInput temperature = null;
122         SensorInput humidity = null;
123         SensorInput outsideTemperature = null;
124         SensorInput outsideHumidity = null;
125         for (SensorInput input : sensors) {
126             if (input.getID().equals(temperatureID)) {
127                 temperature = input;
128             else if (input.getID().equals(humidityID)) {
129                 humidity = input;
130             else if (input.getID().equals(outsideTemperatureID)) {
131                 outsideTemperature = input;
132                 newSensorList.add(input);
133             else if (input.getID().equals(outsideHumidityID)) {
134                 outsideHumidity = input;
135                 newSensorList.add(input);
136             else {
137                 newSensorList.add(input);
138             }
139         }
140         if (temperature == null || humidity == null
141                 || outsideTemperature == null || outsideHumidity == null) {
142             logger.error("consequence(State state=" + state
143                     ") - temperature=" + temperature + ", humidity="
144                     + humidity + ", outsideTemp=" + outsideTemperature
145                     ", outsideHum=" + outsideHumidity
146                     " - needed sensor was null. "
147                     "Returning original input state");
148             return state;
149         }
150         double deltaTemperature = outsideTemperature.getValue()
151                 - temperature.getValue();
152         double deltaHumidity = outsideHumidity.getValue() - humidity.getValue();
153         double newFactor = windowAreaPercentage * (setting / 100)
154                 * SystemSettings.getTimestep();
155         if (Math.abs(newFactor1) {
156             newFactor = Math.signum(newFactor);
157         }
158         double newTemperatureValue = temperature.getValue() + deltaTemperature
159                 * newFactor;
160         newSensorList.add(temperature
161                 .newInstanceWithNewValue(newTemperatureValue));
162         double newHumidityValue = humidity.getValue() + deltaHumidity
163                 * newFactor;
164         newSensorList.add(humidity.newInstanceWithNewValue(newHumidityValue));
165 
166         State returnState = new ClimaticState(newSensorList);
167         if (logger.isDebugEnabled()) {
168             logger.debug("consequence(State state=" + state
169                     ") - end - return value=" + returnState);
170         }
171         return returnState;
172     }
173 
174     /*
175      * (non-Javadoc)
176      
177      * @see dk.deepthought.sidious.supportsystem.Adjustable#possibleAdjustments()
178      */
179     public Collection<Adjustable> possibleAdjustments() {
180         Collection<Adjustable> setpoints = new ArrayList<Adjustable>();
181         for (WindowStep possibleDirection : WindowStep.values()) {
182             double result = setting + possibleDirection.getIncrement();
183             assert result != Float.MAX_VALUE : "result exceeded Float.MAX_VALUE";
184             int min = 0// Minimum value (windows closed)
185             int max = 100// Maximum value (windows opened up all the way)
186             if (result < min) {
187                 result = min;
188                 if (setting != min) {
189                     setpoints.add(new WindowSetPoint(result));
190                 }
191             else if (result > max) {
192                 result = max;
193                 if (setting != max) {
194                     setpoints.add(new WindowSetPoint(result));
195                 }
196             else {
197                 setpoints.add(new WindowSetPoint(result));
198             }
199         }
200         return setpoints;
201     }
202 
203     /*
204      * (non-Javadoc)
205      
206      * @see dk.deepthought.sidious.supportsystem.Adjustable#getID()
207      */
208     public SuperLinkID getID() {
209         return SystemSettings.getWindowSetPointID();
210     }
211 
212     /*
213      * (non-Javadoc)
214      
215      * @see dk.deepthought.sidious.supportsystem.Adjustable#getSetting()
216      */
217     public double getSetting() {
218         return setting;
219     }
220 
221     /*
222      * (non-Javadoc)
223      
224      * @see java.lang.Object#hashCode()
225      */
226     @Override
227     public int hashCode() {
228         final int PRIME = 31;
229         int result = 1;
230         long temp;
231         temp = Double.doubleToLongBits(setting);
232         result = PRIME * result + (int) (temp ^ (temp >>> 32));
233         return result;
234     }
235 
236     /*
237      * (non-Javadoc)
238      
239      * @see java.lang.Object#equals(java.lang.Object)
240      */
241     @Override
242     public boolean equals(Object obj) {
243         if (this == obj)
244             return true;
245         if (obj == null)
246             return false;
247         if (getClass() != obj.getClass())
248             return false;
249         final WindowSetPoint other = (WindowSetPointobj;
250         if (Double.doubleToLongBits(setting!= Double
251                 .doubleToLongBits(other.setting))
252             return false;
253         return true;
254     }
255 
256     /*
257      * (non-Javadoc)
258      
259      * @see java.lang.Object#toString()
260      */
261     @Override
262     public String toString() {
263         return getClass().getSimpleName() "[setting=" + setting + "]";
264     }
265 
266 }