001 package dk.deepthought.sidious.greenhouse;
002
003 import java.util.ArrayList;
004 import java.util.Collection;
005 import java.util.Collections;
006 import java.util.Comparator;
007 import java.util.List;
008
009 import net.jcip.annotations.Immutable;
010
011 import org.apache.commons.logging.Log;
012 import org.apache.commons.logging.LogFactory;
013
014 import dk.deepthought.sidious.supportsystem.State;
015 import dk.deepthought.sidious.supportsystem.SystemSettings;
016
017 /**
018 * This class represents a climatic state of the environment. It is a collection
019 * of environmental sensor inputs.
020 *
021 * @author Deepthought
022 *
023 */
024 @Immutable
025 public class ClimaticState implements State {
026 /**
027 * Logger for this class.
028 */
029 private static final Log logger = LogFactory.getLog(ClimaticState.class);
030
031 /**
032 * The list of sensors.
033 */
034 private final List<SensorInput> sensors;
035
036 /**
037 * This comparator orders sensors by their <code>SuperLinkID</code>.
038 */
039 static final Comparator<SensorInput> SENSOR_ORDER = new Comparator<SensorInput>() {
040 public int compare(SensorInput o1, SensorInput o2) {
041 return o1.getID().compareTo(o2.getID());
042 }
043 };
044
045 /**
046 * Creates a <code>ClimaticState</code> object containing the specified
047 * sensor inputs.
048 *
049 * @param sensors
050 * a collection of sensor inputs related this climatic state
051 */
052 public ClimaticState(Collection<SensorInput> sensors) {
053 this.sensors = new ArrayList<SensorInput>(sensors);
054 }
055
056 /*
057 * (non-Javadoc)
058 *
059 * @see dk.deepthought.sidious.supportsystem.State#impact(java.util.Collection)
060 */
061 public State impact(Collection<State> states) {
062 if (logger.isDebugEnabled()) {
063 logger.debug("impact(Collection<State> states=" + states
064 + ") - start");
065 }
066
067 // Current implementation calculates the average.
068 ClimaticState returnState = average(toClimaticStateList(states));
069 returnState = returnState.incrementTime();
070 if (logger.isDebugEnabled()) {
071 logger.debug("impact(Collection<State> states=" + states
072 + ") - end - return value=" + returnState);
073 }
074 return returnState;
075 }
076
077 /**
078 * Calculates and returns the average <code>State</code>, based on the
079 * input <code>states</code>.
080 * <p>
081 * The method returns <code>null</code>, when presented with
082 * <code>null</code> or an empty collection.
083 *
084 * @param states
085 * states to base calculation on
086 * @return the average state
087 */
088 static ClimaticState average(Collection<ClimaticState> states) {
089 if (logger.isDebugEnabled()) {
090 logger.debug("average(Collection<ClimaticState> states=" + states
091 + ") - start");
092 }
093
094 if ((states == null) || states.isEmpty()) {
095 if (logger.isDebugEnabled()) {
096 logger.debug("average(Collection<ClimaticState> states="
097 + states + ") - end - return value=" + null);
098 }
099 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 }
|