📄 ac_controller.java
字号:
package net.sf.dz.device.actuator.impl;import java.util.Iterator;import java.util.HashSet;import java.util.Set;import org.freehold.jukebox.conf.Configuration;import org.freehold.jukebox.service.ActiveService;import org.freehold.jukebox.service.PassiveService;import org.freehold.jukebox.service.RunnableService;import org.freehold.jukebox.logger.LogAware;import org.freehold.jukebox.logger.LogChannel;import org.freehold.jukebox.util.SimpleQueue;import net.sf.dz.device.actuator.AC;import net.sf.dz.device.actuator.ACDriver;import net.sf.dz.event.ACListener;import net.sf.dz.util.ObjectFactory;/** * The A/C controller implementation. * * Ideally, the only thing missing from this implementation should be the * actual hardware switch drivers. However, the multistage AC may require a * different logic. Let's try to generalize implementation so this one's * enough. * * <h2>Controlling multistage units</h2> * * The single stage system will switch on as soon as the demand rises above * 0, and shut off as soon as it reaches 0 again. The demand is always * positive, and the logic in the zone controller <strong>must</strong> take * the {@link #getMode current operating mode} into account. * * <p> * * For the multistage system, the first stage will do the same, and the * second stage will be controlled by the first entries in {@link * #stageHeating stageHeating} and {@link #stageCooling stageCooling}, and * so on. * * <p> * * One more complication is necessary because the hardware usually doesn't * like the rapid sequence of commands like changing the mode from heat to * cool and back, and changing the stage. In order to eliminate the * possibility of the hardware damage, the state machine introducing the * delays between the control actions is used. * * <p> * * The rules are: * * <ul> * * <li> The command to change the mode will cause the fan and the * compressor to stop and wait for a preset delay if they are not * stopped, then the mode change will be performed. * * <li> The command to change the stage will be executed no sooner than * the preset delay after the last stage change or mode change * operation was carried out. * * <li> The compressor will not switch on before the fan. * * <li> The fan will not switch off before the compressor. * * </ul> * * The actual commands relayed to hardware will have nothing to do with the * abstraction state. If there's a problem with the logic and the * abstraction is oscillating, the hardware will perform the commands, but * with the specified delays, and the size of the queue will grow. * * @author Copyright © <a href="mailto:vt@freehold.crocodile.org"> Vadim Tkachenko</a> 2001 * @version $Id: AC_Controller.java,v 1.8 2004/06/28 20:35:48 vtt Exp $ */public class AC_Controller extends ActiveService implements AC { public static final LogChannel CH_AC = new LogChannel("AC/Controller"); /** * The hardware driver. */ private ACDriver driver; /** * Set of limits to switch to the next heating stage. * * The size of this set determines how many stages does this device * support when heating. Single stage system will have no entries, * two-stage will have one entryand so on. * * @see #stageCooling */ private double stageHeating[] = new double[0]; /** * @see #stageHeating */ private double stageCooling[] = new double[0]; /** * The A/C abstraction status. * * This status is updated immediately. */ private ACState abstraction = new ACState(); /** * The A/C hardware status. * * This status is updated as the commands are carried out. */ private ACState hardware = new ACState(); /** * Delay parameters. */ private Time delay = new Time(); /** * Timestamps. */ private Time timestamp = new Time(); /** * The command queue. */ private TransparentQueue theQueue = new TransparentQueue(); private Set listenerSet = new HashSet(); public int getMode() { return abstraction.mode; } public void setMode(int mode) { if ( abstraction.mode == mode ) { return; } abstraction.mode = mode; enqueue(new ChangeMode(mode)); modeChanged(mode); //complain(LOG_INFO, CH_AC, "Mode set: " + mode); } private synchronized void enqueue(Command command) { // VT: FIXME 2: Turns out that if we go through the iterator, // ConcurrentModificationException may be thrown - I'd guess that is // a result of theQueue.waitObject() in execute() working. Since the // commands are not flushed from the queue anyway, let's make this // code inactive for the moment, and figure out both what to flush // and how to handle the concurrent modification later. if ( false ) { // VT: FIXME 1: See if there are other commands of this kind in // the queue and flush them. Afterthought: this is not so simple // because there may be secondary commands in the queue that // were caused by the primary command. If the primary command is // removed, but secondary commands are not, the result is a // havoc. int offset = 0; for ( Iterator i = theQueue.iterator(); i.hasNext(); ) { Command queuedCommand = (Command)i.next(); complain(LOG_DEBUG, CH_AC, "Queue: @" + (offset++) + ": " + queuedCommand); if ( false ) if ( queuedCommand.getClass().equals(command.getClass()) ) { complain(LOG_INFO, CH_AC, "Queued command flushed: " + queuedCommand); i.remove(); } } complain(LOG_NOTICE, CH_AC, "Queue: @" + offset + ": " + command); } else { complain(LOG_NOTICE, CH_AC, "Queued: " + command); } theQueue.put(command); } protected void configure() throws Throwable { delay.mode = getConfiguration().getLong(getConfigurationRoot() + ".delay.mode", 1000); delay.stage = getConfiguration().getLong(getConfigurationRoot() + ".delay.stage", 1000); delay.fan = getConfiguration().getLong(getConfigurationRoot() + ".delay.fan", 1000); // VT: FIXME: I can understand the settings relevant for the // internal operation of the HVAC unit belonging here, but how // justified is the mode? Shouldn't it be in Unit instead? This will // be a painful change, though... String sMode = getConfiguration().getString(getConfigurationRoot() + ".mode", "cool"); // VT: This is required so the initial mode is properly impressed // upon the hardware (for example, in case when the last instance of // the controller has crashed for some reason and the switches are // still in the running position). // // Now, since we can't talk to the hardware at this point (we have // not been started yet, and the driver is not available), we have // to assign the proper value to the abstraction.mode and remember // to properly set the mode when it is possible (in startup()). if ( "heat".equalsIgnoreCase(sMode) ) { abstraction.mode = 1; } else if ( "cool".equalsIgnoreCase(sMode) ) { abstraction.mode = -1; } else if ( "off".equalsIgnoreCase(sMode) ) { abstraction.mode = 0; } else { complain(LOG_WARNING, CH_AC, "Invalid mode (" + sMode + "), left default (cool)"); abstraction.mode = -1; } // Take care of the driver String driverClassName = getConfiguration().getString(getConfigurationRoot() + ".driver.class"); driver = (ACDriver)ObjectFactory.instantiate(driverClassName, ACDriver.class); if ( driver instanceof LogAware ) { ((LogAware)driver).setLogger(getLogger()); } driver.configure(getConfigurationRoot() + ".driver", getConfiguration()); if ( driver instanceof RunnableService ) { if ( !((RunnableService)driver).start().waitFor() ) { throw new IllegalStateException("Unable to start the driver, check the logs"); } } } public boolean isRunning() { return (abstraction.stage != 0); } /** * Receive a notification about a change in demand. * * The demand change determines the behavior of the unit - the cooler * switches on if the demand raises above 0, the heater switches on as * the demand drops below 0. It depends on the current mode (cooling or * heating) and not necessarily linear. * * @param factor Demand factor. Positive values determine need for * cooling, negative - for heating. */ public synchronized void demand(double factor) { int stage = abstraction.stage; double speed = abstraction.speed; // VT: FIXME: The following describes a single stage system if ( factor > 0 && !isRunning() ) { complain(LOG_NOTICE, CH_AC, "Switching ON " + getModeString()); abstraction.stage = 1; abstraction.speed = 1; } else if ( factor <= 0 && isRunning() ) { complain(LOG_NOTICE, CH_AC, "Switching OFF " + getModeString()); abstraction.stage = 0; abstraction.speed = 0; } demandChanged(factor); // Decide what to do first - compressor or the fan. if ( abstraction.stage == 0 ) { // Need to process the stage first - we're shutting off if ( abstraction.stage != stage ) { enqueue(new ChangeStage(abstraction.stage)); stageChanged(abstraction.stage); } if ( abstraction.speed != speed ) { enqueue(new ChangeFanSpeed(abstraction.speed)); fanSpeedChanged(abstraction.speed); } } else { // Need to process the fan first - we're starting up if ( abstraction.speed != speed ) { enqueue(new ChangeFanSpeed(abstraction.speed)); fanSpeedChanged(abstraction.speed); } if ( abstraction.stage != stage ) { enqueue(new ChangeStage(abstraction.stage)); stageChanged(abstraction.stage); } } } private String getModeString() { StringBuffer sb = new StringBuffer("(mode: "); switch ( abstraction.mode ) { case -1: sb.append("cooling"); break; case 0: sb.append("off"); break; case 1: sb.append("heating"); break; default: throw new IllegalStateException("Invalid abstraction mode: " + abstraction.mode); } sb.append(")"); return sb.toString(); } /** * Add the listener. * * @param listener The object to be notified about the state changes. */ public final void addListener(ACListener listener) { listenerSet.add(listener); } /** * Remove the listener. */ public final void removeListener(ACListener listener) { listenerSet.remove(listener); } /** * Broadcast the notification about the change in demand. * * @param value New demand value. */ private final void demandChanged(double value) { for ( Iterator i = listenerSet.iterator(); i.hasNext(); ) { ((ACListener)i.next()).demandChanged(this, value);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -