The HVAC Rules
(Note: The rule design was lifted directly out of JIA, so I won't make any attempt to explain the whys or alternatives. I recommend that you read JIA, its an excellent book.)
The rules are seperated into two groups, or rule-sets. One set for controlling the heat pumps, and another for controlling the vents.
Controlling the Heat PumpsThere are four rules that control the heat pumps. PumpOffWhenFloorsCoolEnough and PumpOffWhenFloorsWarnEnough ensure that the heat pump is turned off when a floor's tempurature reaches the desired level (ie, the setPoint defined by TempuratureControl). PumpCoolingWhenFloorTooHot, sets the pump state to COOLING when the floor becomes too hot. Remember that "too hot" means that the tempurature has passed the setPoint by guardAmount. In other words, we don't want to turn the pump on immediatly after desired tempurature has been reached to avoid hystersis and thus damaging the pump. Similary, PumpHeatingWhenFloorTooCold sets the pump state to HEATING when the floor becomes too cold.
Lets build PumpCoolingWhenFloorTooHot first. The first step is to create a POJO class annotated with @Rule.
The @Rule annotation declares the POJO to be a drools rule. @Rule allows for rule metadata to be declared in the annotation. For example:
As we will soon see, the conditions in this rule will need access to the buildings TemperatureControl. Recall from the domain model that TemperatureControl maintains the buildings desired tempurature, as well as exporting methods that determine if a given temperature is too cold, too hot, etc. We declare TemperatureControl as a java beans property on the rule. We'll see how this property get injected into the rule when we cover the spring configuration.
Next comes the condition methods. We only want this rule to fire (ie, turn the pump to COOLING) if the pump is currently in the OFF state.
The @Condition annotation declares a method to be a rule condition. The method must have the signature "boolean *(..)".
Condition methods are invoked by the drools rete engine, therefore the arguments passed to them must be known to the engine. Three types of condition parameters are supported: facts, application-data, and the special class KnowledgeHelper. When using annotations, facts are specified using the @Fact parameter annotation. Drools-spring can also infer condition argument types using various pluggable strageties. Throughout the HVAC application, all condition parameters will be facts, so we will omit @Fact and specify an appropriate strategy in the spring configuration (covered later on).
The next condition for this rule is that the Thermometer tempurature reading "is too hot". We check this by invoking the method TempuratureControl.isTooHot(double tempurature).
Recall from the domain model that a HeatPump serves three adjacent Floors. So we need to ensure that the HeatPump we determined to be OFF is serving the the Floor of the Thermometer instance checked by the isTooHot condition method.
You might be tempted to think that we are doing to much work by checking too many permutations. But because of the way Rete networks are built, a condition is tested for a given set of facts only once, and the calculated result stored until a fact is actually modified.
Finally, we need to specify the rule consequence. When our conditions have determined that the floor's temperature is too hot, and the floor's heat pump is off, then we set the heat pump cooling.
The @Consequence annotations marks the method as a rule consequence. And here again we see HeatPump specified as a parameter. Similar to conditions, the spring configuration will be setup such that the consequence parameters without annotations will be considered facts. Since HeatPump is a fact asserted into working-memory, the change in its state will propagate through the Rete network, causing conditions to fire, and possibly activating other rules.
Here is complete code for PumpOffWhenFloorsCoolEnough.
The other three heat-pump rules are simialar and should be self explainitory:
PumpOffWhenFloorsWarmEnough,
PumpCoolingWhenFloorTooHot,
PumpHeatingWhenFloorTooCold.
TODO
