1 | package dk.deepthought.sidious.greenhouse; |
2 | |
3 | import java.util.ArrayList; |
4 | import java.util.Collection; |
5 | import java.util.Collections; |
6 | import java.util.Comparator; |
7 | import java.util.List; |
8 | |
9 | import net.jcip.annotations.Immutable; |
10 | |
11 | import org.apache.commons.logging.Log; |
12 | import org.apache.commons.logging.LogFactory; |
13 | |
14 | import dk.deepthought.sidious.supportsystem.State; |
15 | import dk.deepthought.sidious.supportsystem.SystemSettings; |
16 | |
17 | /** |
18 | * This class represents a climatic state of the environment. It is a collection |
19 | * of environmental sensor inputs. |
20 | * |
21 | * @author Deepthought |
22 | * |
23 | */ |
24 | @Immutable |
25 | public class ClimaticState implements State { |
26 | /** |
27 | * Logger for this class. |
28 | */ |
29 | private static final Log logger = LogFactory.getLog(ClimaticState.class); |
30 | |
31 | /** |
32 | * The list of sensors. |
33 | */ |
34 | private final List<SensorInput> sensors; |
35 | |
36 | /** |
37 | * This comparator orders sensors by their <code>SuperLinkID</code>. |
38 | */ |
39 | static final Comparator<SensorInput> SENSOR_ORDER = new Comparator<SensorInput>() { |
40 | public int compare(SensorInput o1, SensorInput o2) { |
41 | return o1.getID().compareTo(o2.getID()); |
42 | } |
43 | }; |
44 | |
45 | /** |
46 | * Creates a <code>ClimaticState</code> object containing the specified |
47 | * sensor inputs. |
48 | * |
49 | * @param sensors |
50 | * a collection of sensor inputs related this climatic state |
51 | */ |
52 | public ClimaticState(Collection<SensorInput> sensors) { |
53 | this.sensors = new ArrayList<SensorInput>(sensors); |
54 | } |
55 | |
56 | /* |
57 | * (non-Javadoc) |
58 | * |
59 | * @see dk.deepthought.sidious.supportsystem.State#impact(java.util.Collection) |
60 | */ |
61 | public State impact(Collection<State> states) { |
62 | if (logger.isDebugEnabled()) { |
63 | logger.debug("impact(Collection<State> states=" + states |
64 | + ") - start"); |
65 | } |
66 | |
67 | // Current implementation calculates the average. |
68 | ClimaticState returnState = average(toClimaticStateList(states)); |
69 | returnState = returnState.incrementTime(); |
70 | if (logger.isDebugEnabled()) { |
71 | logger.debug("impact(Collection<State> states=" + states |
72 | + ") - end - return value=" + returnState); |
73 | } |
74 | return returnState; |
75 | } |
76 | |
77 | /** |
78 | * Calculates and returns the average <code>State</code>, based on the |
79 | * input <code>states</code>. |
80 | * <p> |
81 | * The method returns <code>null</code>, when presented with |
82 | * <code>null</code> or an empty collection. |
83 | * |
84 | * @param states |
85 | * states to base calculation on |
86 | * @return the average state |
87 | */ |
88 | static ClimaticState average(Collection<ClimaticState> states) { |
89 | if (logger.isDebugEnabled()) { |
90 | logger.debug("average(Collection<ClimaticState> states=" + states |
91 | + ") - start"); |
92 | } |
93 | |
94 | if ((states == null) || states.isEmpty()) { |
95 | if (logger.isDebugEnabled()) { |
96 | logger.debug("average(Collection<ClimaticState> states=" |
97 | + states + ") - end - return value=" + null); |
98 | } |
99 | throw new IllegalArgumentException("States list cannot be: " + states); |
100 | } |
101 | int size = states.size(); |
102 | ClimaticState average = null; |
103 | ClimaticState cs = total(new ArrayList<ClimaticState>(states), average); |
104 | ArrayList<SensorInput> sensors = new ArrayList<SensorInput>(cs |
105 | .getSensors()); |
106 | for (int i = 0, s = sensors.size(); i < s; i++) { |
107 | SensorInput sensor = sensors.remove(i); |
108 | sensors.add(i, sensor.newInstanceWithNewValue(sensor.getValue() |
109 | / size)); |
110 | } |
111 | ClimaticState returnClimaticState = new ClimaticState(sensors); |
112 | if (logger.isDebugEnabled()) { |
113 | logger.debug("average(Collection<ClimaticState> states=" + states |
114 | + ") - end - return value=" + returnClimaticState); |
115 | } |
116 | return returnClimaticState; |
117 | } |
118 | |
119 | /** |
120 | * This method converts the input <code>State</code> objects to a list of |
121 | * <code>ClimaticState</code> objects. |
122 | * |
123 | * @param states |
124 | * input states to be concerted |
125 | * @return the input states as climatic states |
126 | */ |
127 | static ArrayList<ClimaticState> toClimaticStateList(Collection<State> states) { |
128 | if (logger.isDebugEnabled()) { |
129 | logger.debug("toClimaticStateList(Collection<State> states=" |
130 | + states + ") - start"); |
131 | } |
132 | |
133 | if ((states == null) || states.isEmpty()) { |
134 | ArrayList<ClimaticState> returnArrayList = new ArrayList<ClimaticState>(); |
135 | if (logger.isDebugEnabled()) { |
136 | logger.debug("toClimaticStateList(Collection<State> states=" |
137 | + states + ") - end - return value=" + returnArrayList); |
138 | } |
139 | return returnArrayList; |
140 | } |
141 | ArrayList<ClimaticState> returnStates = new ArrayList<ClimaticState>(); |
142 | for (State state : states) { |
143 | if (state instanceof ClimaticState) { |
144 | returnStates.add((ClimaticState) state); |
145 | } |
146 | } |
147 | |
148 | if (logger.isDebugEnabled()) { |
149 | logger.debug("toClimaticStateList(Collection<State> states=" |
150 | + states + ") - end - return value=" + returnStates); |
151 | } |
152 | return returnStates; |
153 | } |
154 | |
155 | /** |
156 | * This method calculates the total sum of all related sensor inputs in the |
157 | * <code>states</code>. The sum is calculated recursively. |
158 | * <p> |
159 | * The method returns <code>null</code>, when presented with |
160 | * <code>null</code> or an empty collection. |
161 | * |
162 | * @param states |
163 | * the list of climatic states to be summed |
164 | * @param total |
165 | * the parameter in which the sum will be calculated; is needed |
166 | * for recursive purposes |
167 | * @return a <code>ClimaticState</code> representing the total sum of the |
168 | * related sensor inputs |
169 | */ |
170 | static ClimaticState total(ArrayList<ClimaticState> states, |
171 | ClimaticState total) { |
172 | if (logger.isDebugEnabled()) { |
173 | logger.debug("total(ArrayList<ClimaticState> states=" + states |
174 | + ", ClimaticState total=" + total + ") - start"); |
175 | } |
176 | |
177 | if ((states == null) || states.isEmpty()) { |
178 | if (logger.isDebugEnabled()) { |
179 | logger.debug("total(ArrayList<ClimaticState> states=" + states |
180 | + ", ClimaticState total=" + total |
181 | + ") - end - return value=" + null); |
182 | } |
183 | return null; |
184 | } |
185 | ArrayList<ClimaticState> listOfCS = new ArrayList<ClimaticState>(states); |
186 | ClimaticState currentCS = listOfCS.remove(0); |
187 | if (listOfCS.isEmpty()) { |
188 | total = currentCS.sum(total); |
189 | |
190 | if (logger.isDebugEnabled()) { |
191 | logger.debug("total(ArrayList<ClimaticState> states=" + states |
192 | + ", ClimaticState total=" + total |
193 | + ") - end - return value=" + total); |
194 | } |
195 | return total; |
196 | } else { |
197 | ClimaticState returnClimaticState = total(listOfCS, currentCS).sum( |
198 | total); |
199 | if (logger.isDebugEnabled()) { |
200 | logger.debug("total(ArrayList<ClimaticState> states=" + states |
201 | + ", ClimaticState total=" + total |
202 | + ") - end - return value=" + returnClimaticState); |
203 | } |
204 | return returnClimaticState; |
205 | } |
206 | } |
207 | |
208 | /** |
209 | * This method calculates the sum of this <code>ClimaticState</code> and |
210 | * <code>other</code>. |
211 | * <p> |
212 | * The method returns a deep-copy of <code>this</code>, when presented |
213 | * with <code>null</code>. |
214 | * |
215 | * @param other |
216 | * the other climatic state |
217 | */ |
218 | ClimaticState sum(ClimaticState other) { |
219 | if (logger.isDebugEnabled()) { |
220 | logger.debug("sum(ClimaticState other=" + other + ") - start"); |
221 | } |
222 | |
223 | if (other == null) { |
224 | ClimaticState returnClimaticState = new ClimaticState(getSensors()); |
225 | if (logger.isDebugEnabled()) { |
226 | logger.debug("sum(ClimaticState other=null)" |
227 | + " - end - return value=" + returnClimaticState); |
228 | } |
229 | return returnClimaticState; |
230 | } |
231 | Collection<SensorInput> sensorList = new ArrayList<SensorInput>(); |
232 | for (SensorInput sensor : sensors) { |
233 | double total = sensor.getValue(); |
234 | for (SensorInput input : other.getSensors()) { |
235 | if (sensor.equalsOnSuperLinkID(input)) { |
236 | total += input.getValue(); |
237 | } |
238 | } |
239 | sensorList.add(sensor.newInstanceWithNewValue(total)); |
240 | } |
241 | ClimaticState returnClimaticState = new ClimaticState(sensorList); |
242 | if (logger.isDebugEnabled()) { |
243 | logger.debug("sum(ClimaticState other=" + other |
244 | + ") - end - return value=" + returnClimaticState); |
245 | } |
246 | return returnClimaticState; |
247 | } |
248 | |
249 | /* |
250 | * (non-Javadoc) |
251 | * |
252 | * @see dk.deepthought.sidious.supportsystem.State#sameStateSpace(dk.deepthought.sidious.supportsystem.State) |
253 | */ |
254 | public boolean sameStateSpace(State other) { |
255 | if (other instanceof ClimaticState) { |
256 | ArrayList<SensorInput> otherSensors = new ArrayList<SensorInput>( |
257 | ((ClimaticState) other).getSensors()); |
258 | if (otherSensors.size() != sensors.size()) { |
259 | return false; |
260 | } |
261 | Collections.sort(sensors, SENSOR_ORDER); |
262 | Collections.sort(otherSensors, SENSOR_ORDER); |
263 | for (int i = 0; i < sensors.size(); i++) { |
264 | if (!sensors.get(i).equalsOnSuperLinkID(otherSensors.get(i))) { |
265 | return false; |
266 | } |
267 | } |
268 | return true; |
269 | } |
270 | return false; |
271 | } |
272 | |
273 | /* (non-Javadoc) |
274 | * @see dk.deepthought.sidious.supportsystem.State#partiallyEquals(dk.deepthought.sidious.supportsystem.State) |
275 | */ |
276 | public boolean partiallyEquals(State state) { |
277 | if (state instanceof ClimaticState) { |
278 | ClimaticState climaticState = (ClimaticState) state; |
279 | for (SensorInput sensor : climaticState.getSensors()) { |
280 | if (!sensors.contains(sensor)) { |
281 | return false; |
282 | } |
283 | } |
284 | return true; |
285 | } |
286 | return false; |
287 | } |
288 | |
289 | /** |
290 | * Gets a <code>Collection</code> of <code>SensorInput</code> |
291 | * |
292 | * @return the sensors related to this climatic state |
293 | */ |
294 | public Collection<SensorInput> getSensors() { |
295 | if (logger.isDebugEnabled()) { |
296 | logger.debug("getSensors() - start"); |
297 | } |
298 | |
299 | Collection<SensorInput> returnCollection = new ArrayList<SensorInput>( |
300 | sensors); |
301 | if (logger.isDebugEnabled()) { |
302 | logger.debug("getSensors() - end - return value=" |
303 | + returnCollection); |
304 | } |
305 | return returnCollection; |
306 | } |
307 | |
308 | /** |
309 | * Increments the time of this. |
310 | * @return the time incremented version of this. |
311 | */ |
312 | private ClimaticState incrementTime() { |
313 | ArrayList<SensorInput> newSensors = new ArrayList<SensorInput>(); |
314 | for (SensorInput sensor : sensors) { |
315 | if (SystemSettings.getTimeID().equals(sensor.getID())) { |
316 | sensor = sensor.newInstanceWithNewValue(sensor.getValue() |
317 | + SystemSettings.getTimestep()); |
318 | } |
319 | newSensors.add(sensor); |
320 | } |
321 | return new ClimaticState(newSensors); |
322 | } |
323 | |
324 | /* |
325 | * (non-Javadoc) |
326 | * |
327 | * @see java.lang.Object#equals(java.lang.Object) |
328 | */ |
329 | @Override |
330 | public boolean equals(Object obj) { |
331 | if (this == obj) { |
332 | return true; |
333 | } |
334 | if (obj == null) { |
335 | return false; |
336 | } |
337 | if (getClass() != obj.getClass()) { |
338 | return false; |
339 | } |
340 | final ClimaticState other = (ClimaticState) obj; |
341 | if (sensors == null) { |
342 | if (other.sensors != null) { |
343 | return false; |
344 | } |
345 | } else if (!sensors.equals(other.sensors)) { |
346 | return false; |
347 | } |
348 | return true; |
349 | } |
350 | |
351 | /* |
352 | * (non-Javadoc) |
353 | * |
354 | * @see java.lang.Object#hashCode() |
355 | */ |
356 | @Override |
357 | public int hashCode() { |
358 | final int PRIME = 31; |
359 | int result = 1; |
360 | result = PRIME * result + ((sensors == null) ? 0 : sensors.hashCode()); |
361 | return result; |
362 | } |
363 | |
364 | /* |
365 | * (non-Javadoc) |
366 | * |
367 | * @see java.lang.Object#toString() |
368 | */ |
369 | @Override |
370 | public String toString() { |
371 | return getClass().getSimpleName() + "[sensors=" + sensors + "]"; |
372 | } |
373 | |
374 | } |