001 package dk.deepthought.sidious.rules;
002
003 import java.util.ArrayList;
004 import java.util.Collection;
005 import java.util.Properties;
006
007 import org.apache.commons.logging.Log;
008 import org.apache.commons.logging.LogFactory;
009
010 import dk.deepthought.sidious.goalhandler.Goal;
011 import dk.deepthought.sidious.greenhouse.ClimaticState;
012 import dk.deepthought.sidious.greenhouse.SensorInput;
013 import dk.deepthought.sidious.services.ServiceEngine;
014 import dk.deepthought.sidious.supportsystem.State;
015 import dk.deepthought.sidious.supportsystem.Step;
016 import dk.deepthought.sidious.supportsystem.SuperLinkID;
017 import dk.deepthought.sidious.supportsystem.SystemSettings;
018 import dk.deepthought.sidious.util.RuleProperty;
019
020 /**
021 * This class represents a temperature rule.
022 * <p>
023 * The rule advocates for maintaining a mean temperature, and avoiding
024 * temperature boundaries.
025 *
026 * @author Deepthought
027 *
028 */
029 public final class TemperatureRule extends Rule {
030
031 private static final Log logger = LogFactory.getLog(TemperatureRule.class);
032
033 /**
034 * The ID of the sensor this Rule depends on.
035 */
036 private final SuperLinkID SENSOR_ID;
037
038 /**
039 * The RuleProperty of this class
040 */
041 private static RuleProperty ruleProperty;
042
043 /**
044 * The mean temperature.
045 */
046 private final double T_MEAN;
047
048 /**
049 * The max temperature.
050 */
051 private final double T_MAX;
052
053 /**
054 * Coefficient for mathematical calculations
055 */
056 private final double K_MAX;
057
058 /**
059 * Coefficient for mathematical calculations
060 */
061 private final double K_MIN;
062
063 /**
064 * The min temperature.
065 */
066 private final double T_MIN;
067
068 /**
069 * Arbitrary max value.
070 */
071 private static final double MAXVALUE = 1000;
072
073 /**
074 * Constructor.
075 *
076 * @param parentId
077 * the id of the parent <code>PlanRequester</code>
078 */
079 public TemperatureRule(final SuperLinkID parentId) {
080 if (parentId == null) {
081 logger.error("TemperatureRule(SuperLinkID parentId=null) "
082 + "- not valid input");
083 throw new IllegalArgumentException("parentID=null not valid input");
084 }
085 if (ruleProperty == null) {
086 ruleProperty = new RuleProperty(this.getClass().getSimpleName());
087 }
088 SENSOR_ID = SystemSettings.getTemperatureID();
089 //FIXME hent fra property igen!
090 // SENSOR_ID = ruleProperty.getID("sensor_id");
091 T_MEAN = ruleProperty.getFloat("t_mean", 20);
092 T_MAX = ruleProperty.getFloat("t_max", 30);
093 K_MAX = ruleProperty.getFloat("k_max", 0.5f);
094 K_MIN = ruleProperty.getFloat("k_min", 0.5f);
095 T_MIN = ruleProperty.getFloat("t_min", 5);
096 setParentID(parentId);
097 if (logger.isDebugEnabled()) {
098 logger.debug("TemperatureRule(SuperLinkID parentId=" + parentId
099 + ") - SENSOR_ID=" + SENSOR_ID + ", T_MEAN=" + T_MEAN
100 + ", T_MAX=" + T_MAX + ", K_MAX=" + K_MAX + ", K_MIN="
101 + K_MIN + ", T_MIN=" + T_MIN);
102 }
103 }
104
105 /**
106 * Static factory for constructing a TemperatureRule with the specified
107 * properties.
108 *
109 * @param parentID
110 * the id of the parent <code>PlanRequester</code>
111 * @param properties
112 * the properties
113 * @return a new TemperatureRule from the given properties
114 */
115 public static TemperatureRule constructTemperatureRule(
116 SuperLinkID parentID, Properties properties) {
117 ruleProperty = new RuleProperty(properties);
118 return new TemperatureRule(parentID);
119 }
120
121 /*
122 * (non-Javadoc)
123 *
124 * @see dk.deepthought.sidious.rules.Rule#desire(dk.deepthought.sidious.supportsystem.State,
125 * dk.deepthought.sidious.supportsystem.State)
126 */
127 public double desire(State currentState, State newState, Step step) {
128 if (logger.isDebugEnabled()) {
129 logger.debug("desire(State currentState=" + currentState
130 + ", State newState=" + newState + ") - start");
131 }
132
133 double defaultValue = 0;
134 if (currentState == null || newState == null) {
135 logger.error("desire(State currentState=" + currentState
136 + ", State newState=" + newState + ") - currentState="
137 + currentState + ", newState=" + newState
138 + " - null not valid input");
139 return defaultValue;
140 }
141 Collection<SensorInput> curSensors = ((ClimaticState) currentState)
142 .getSensors();
143 Collection<SensorInput> newSensors = ((ClimaticState) newState)
144 .getSensors();
145 SensorInput curTempSensor = null;
146 SensorInput newTempSensor = null;
147 for (SensorInput input : curSensors) {
148 if (input.getID().equals(SENSOR_ID)) {
149 curTempSensor = input;
150 }
151 }
152 for (SensorInput input : newSensors) {
153 if (input.getID().equals(SENSOR_ID)) {
154 newTempSensor = input;
155 }
156 }
157 if (curTempSensor == null || newTempSensor == null) {
158 if (logger.isDebugEnabled()) {
159 logger.debug("desire(State currentState=" + currentState
160 + ", State newState=" + newState
161 + ") - end - return defaultValue=" + defaultValue);
162 }
163 return defaultValue;
164 }
165 double returndouble = total(curTempSensor.getValue(), newTempSensor
166 .getValue());
167 if (logger.isDebugEnabled()) {
168 logger
169 .debug("desire(State currentState=" + currentState + ", State newState=" + newState + ") - end - return value=" + returndouble); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
170 }
171 return returndouble;
172 }
173
174 /**
175 * Method returns the calculated total desire of the input temperatures.
176 *
177 * @param curTemperature
178 * current temperature
179 * @param newTemperature
180 * the new temperature
181 * @return the combined total desire
182 */
183 double total(double curTemperature, double newTemperature) {
184 if (newTemperature > T_MAX || newTemperature < T_MIN) {
185 return MAXVALUE;
186 }
187 double newDesire = calculateDesire(newTemperature);
188 double combinedDesire = newDesire - calculateDesire(curTemperature);
189 if (combinedDesire < 0) {
190 return newDesire;
191 }
192 return Math.abs(combinedDesire);
193 }
194
195 /**
196 * This method calculates the desire for a given temperature.
197 *
198 * @param temperature
199 * the temperature
200 * @return the desire for the given temperature
201 */
202 double calculateDesire(double temperature) {
203 if (temperature > T_MAX || temperature < T_MIN) {
204 return MAXVALUE; // Large value due to plant fatality
205 }
206 double dMax = Math.pow(K_MAX, Math.abs(T_MAX - temperature));
207 double dMin = Math.pow(K_MIN, Math.abs(temperature - T_MIN));
208 double dMean = Math.abs((T_MEAN - temperature) / (T_MAX - T_MIN));
209 // Rounding to eliminate negligible values
210 double maxPower = Math.max(dMin, dMax);
211 if (maxPower < 0.001) {
212 maxPower = 0;
213 }
214 return (double) Math.max(maxPower, dMean);
215 }
216
217 /*
218 * (non-Javadoc)
219 *
220 * @see dk.deepthought.sidious.rules.Rule#getGoals()
221 */
222 public Collection<Goal> getGoals() {
223 SensorInput sensorInput = new SensorInput(SENSOR_ID, T_MEAN);
224 ArrayList<SensorInput> list = new ArrayList<SensorInput>();
225 list.add(sensorInput);
226 double currentTemperature = ServiceEngine.getSensorValue(SENSOR_ID);
227 State goalState = new ClimaticState(list);
228 double calculateDesire = calculateDesire(currentTemperature);
229 Goal g = new Goal(goalState, calculateDesire, getParentID());
230 ArrayList<Goal> goalList = new ArrayList<Goal>();
231 goalList.add(g);
232 return goalList;
233 }
234
235 }
|