1 | package dk.deepthought.sidious.greenhouse; |
2 | |
3 | import java.util.ArrayList; |
4 | import java.util.Collection; |
5 | |
6 | import net.jcip.annotations.Immutable; |
7 | |
8 | import org.apache.commons.logging.Log; |
9 | import org.apache.commons.logging.LogFactory; |
10 | |
11 | import dk.deepthought.sidious.supportsystem.Adjustable; |
12 | import dk.deepthought.sidious.supportsystem.State; |
13 | import dk.deepthought.sidious.supportsystem.SuperLinkID; |
14 | import dk.deepthought.sidious.supportsystem.SystemSettings; |
15 | |
16 | /** |
17 | * This class represents the setpoint for the greenhouse windows. |
18 | * |
19 | * @author Deepthought |
20 | * |
21 | */ |
22 | @Immutable |
23 | public class WindowSetPoint implements Adjustable { |
24 | /** |
25 | * Logger for this class |
26 | */ |
27 | private static final Log logger = LogFactory.getLog(WindowSetPoint.class); |
28 | |
29 | /** |
30 | * The setting of this setpoint. |
31 | */ |
32 | private final double setting; |
33 | |
34 | /** |
35 | * The id of the temperature sensor. |
36 | */ |
37 | private final SuperLinkID temperatureID; |
38 | |
39 | /** |
40 | * The id of the humidity sensor. |
41 | */ |
42 | private final SuperLinkID humidityID; |
43 | |
44 | /** |
45 | * The id of the outside temperature sensor. |
46 | */ |
47 | private final SuperLinkID outsideTemperatureID; |
48 | |
49 | /** |
50 | * The id of the outside humidity sensor. |
51 | */ |
52 | private final SuperLinkID outsideHumidityID; |
53 | |
54 | /** |
55 | * Approximate window surface percentage. |
56 | */ |
57 | private static final int windowAreaPercentage = 10; |
58 | |
59 | /** |
60 | * Internal enum to describe possible adjustments. |
61 | */ |
62 | private enum WindowStep { |
63 | OPEN(5d), CLOSE(-5d); |
64 | |
65 | private double increment; |
66 | |
67 | WindowStep(double increment) { |
68 | this.increment = increment; |
69 | } |
70 | |
71 | public double getIncrement() { |
72 | return increment; |
73 | } |
74 | } |
75 | |
76 | /** |
77 | * Creates a new <code>ScreenSetPoint</code> with the specified setting. |
78 | * <p> |
79 | * Input parameters should be between 0 and 100 percent. Parameters outside |
80 | * this will be rounded to nearest limit. |
81 | * |
82 | * @param setting |
83 | * the setting |
84 | */ |
85 | public WindowSetPoint(double setting) { |
86 | humidityID = SystemSettings.getHumidityID(); |
87 | temperatureID = SystemSettings.getTemperatureID(); |
88 | outsideHumidityID = SystemSettings.getOutsideHumidityID(); |
89 | outsideTemperatureID = SystemSettings.getOutsideTemperatureID(); |
90 | if (setting > 100) { |
91 | setting = 100; |
92 | } |
93 | if (setting < 0) { |
94 | setting = 0; |
95 | } |
96 | this.setting = setting; |
97 | } |
98 | |
99 | /* |
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 = (ClimaticState) state; |
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(newFactor) > 1) { |
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 = (WindowSetPoint) obj; |
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 | } |