diff --git a/modules/hcc-bootstrap/src/main/java/net/sf/dz3r/runtime/config/model/ZoneConfigurationParser.java b/modules/hcc-bootstrap/src/main/java/net/sf/dz3r/runtime/config/model/ZoneConfigurationParser.java index 2a32c5bb0..9816027aa 100644 --- a/modules/hcc-bootstrap/src/main/java/net/sf/dz3r/runtime/config/model/ZoneConfigurationParser.java +++ b/modules/hcc-bootstrap/src/main/java/net/sf/dz3r/runtime/config/model/ZoneConfigurationParser.java @@ -3,6 +3,7 @@ import net.sf.dz3r.common.HCCObjects; import net.sf.dz3r.device.actuator.economizer.EconomizerConfig; import net.sf.dz3r.device.actuator.economizer.EconomizerContext; +import net.sf.dz3r.device.actuator.economizer.EconomizerSettings; import net.sf.dz3r.model.Range; import net.sf.dz3r.model.Thermostat; import net.sf.dz3r.model.Zone; @@ -78,12 +79,15 @@ private EconomizerContext createEconomizer(String zoneName, net.sf.dz3r.runtime. return new EconomizerContext( new EconomizerConfig( cf.mode(), - cf.settings().changeoverDelta(), - cf.settings().targetTemperature(), - cf.settings().keepHvacOn(), cf.controller().p(), cf.controller().i(), - cf.controller().limit()), + cf.controller().limit(), + new EconomizerSettings( + cf.settings().changeoverDelta(), + cf.settings().targetTemperature(), + cf.settings().keepHvacOn() + ) + ), ambientSensor, hvacDevice, timeout); diff --git a/modules/hcc-influxdb/src/main/java/net/sf/dz3r/view/influxdb/v3/ZoneMetricsConverter.java b/modules/hcc-influxdb/src/main/java/net/sf/dz3r/view/influxdb/v3/ZoneMetricsConverter.java index 2c1ea7400..8c79770b1 100644 --- a/modules/hcc-influxdb/src/main/java/net/sf/dz3r/view/influxdb/v3/ZoneMetricsConverter.java +++ b/modules/hcc-influxdb/src/main/java/net/sf/dz3r/view/influxdb/v3/ZoneMetricsConverter.java @@ -63,9 +63,15 @@ private Mono convertEconomizer(Signal signal) { var economizerStatus = status.economizerStatus; - b.addField("enabled", economizerStatus.settings.enabled); - b.addField("delta", economizerStatus.settings.changeoverDelta); - b.addField("target", economizerStatus.settings.targetTemperature); + if (economizerStatus.settings != null) { + + b.addField("enabled", true); + b.addField("delta", economizerStatus.settings.changeoverDelta); + b.addField("target", economizerStatus.settings.targetTemperature); + + } else { + b.addField("enabled", false); + } b.addField("calling", economizerStatus.callingStatus.calling); b.addField("demand", economizerStatus.callingStatus.demand); diff --git a/modules/hcc-model/src/main/java/net/sf/dz3r/device/actuator/economizer/AbstractEconomizer.java b/modules/hcc-model/src/main/java/net/sf/dz3r/device/actuator/economizer/AbstractEconomizer.java index ed9867edb..edea5e6af 100644 --- a/modules/hcc-model/src/main/java/net/sf/dz3r/device/actuator/economizer/AbstractEconomizer.java +++ b/modules/hcc-model/src/main/java/net/sf/dz3r/device/actuator/economizer/AbstractEconomizer.java @@ -38,7 +38,7 @@ public abstract class AbstractEconomizer implements SignalProcessor logger.debug("{}: HVAC device state/done: {}", getAddress(), s)); this.economizerStatus = new EconomizerStatus( - new EconomizerSettings(settings), + new EconomizerSettings(config.settings), null, 0, false, null); // Don't forget to connect fluxes; this can only be done in subclasses after all the @@ -119,14 +119,14 @@ private void checkModes(HvacMode mode, HvacDevice device) { } } - public void setSettings(EconomizerConfig settings) { - if (settings == null) { + public void setSettings(EconomizerConfig config) { + if (config == null) { throw new IllegalArgumentException("settings can't be null"); } - this.settings = this.settings == null ? settings : this.settings.merge(settings); + this.config = this.config == null ? config : this.config.merge(config); - logger.info("{}: setSettings(): {}", getAddress(), settings); + logger.info("{}: setSettings(): {}", getAddress(), config); } @Override @@ -172,7 +172,7 @@ private void setDeviceState(Boolean state) { var ctl = Boolean.TRUE.equals(state) ? 1.0 : 0.0; var signal = new Signal( clock.instant(), - new HvacCommand(settings.mode, ctl, ctl) + new HvacCommand(config.mode, ctl, ctl) ); logger.debug("{}: setDeviceState={}", getAddress(), signal); @@ -206,7 +206,7 @@ private Boolean recordDeviceState(Signal computeCombined(IndoorAmbientPair pair) { logger.trace("{}: raw {}", getAddress(), pair); + // Shortcut: are we enabled to begin with? + + if (config.isEnabled()) { + + logger.trace("not enabled, bailing out with 0"); + return new Signal<>(clock.instant(), 0d); + } + // Let's take care of corner cases first if (pair.indoor == null || pair.ambient == null) { @@ -325,7 +333,7 @@ protected double computeCombined(Double indoorTemperature, Double ambientTempera double targetAdjustment; - if (targetDelta > settings.changeoverDelta) { + if (targetDelta > config.settings.changeoverDelta) { // We're still above the target targetAdjustment = 0.0; @@ -335,13 +343,13 @@ protected double computeCombined(Double indoorTemperature, Double ambientTempera // We're below the target, but the mode adjusted ambient is too high - this is an abnormal situation, // either the target is misconfigured, or someone pulled the setpoint too far - logger.warn("{}: economizer abnormal, indoor={}, ambient={}, settings={}", getAddress(), indoorTemperature, ambientTemperature, settings); + logger.warn("{}: economizer abnormal, indoor={}, ambient={}, settings={}", getAddress(), indoorTemperature, ambientTemperature, config); targetAdjustment = 0.0; } else { // As the indoor temperature is approaching the target, need to take corrective measures - var k = (1.0 / settings.changeoverDelta) * (settings.changeoverDelta - targetDelta); + var k = (1.0 / config.settings.changeoverDelta) * (config.settings.changeoverDelta - targetDelta); targetAdjustment = ambientDelta * k; @@ -356,26 +364,26 @@ protected double computeCombined(Double indoorTemperature, Double ambientTempera } /** - * Get the {@link EconomizerConfig#changeoverDelta ambient} delta signal. + * Get the {@link EconomizerSettings#changeoverDelta ambient} delta signal. * * @return Positive value indicates demand, negative indicates lack thereof, regardless of mode. */ double getAmbientDelta(double indoor, double ambient) { - return settings.mode == HvacMode.COOLING - ? indoor - (ambient + settings.changeoverDelta) - : ambient - (indoor + settings.changeoverDelta); + return config.mode == HvacMode.COOLING + ? indoor - (ambient + config.settings.changeoverDelta) + : ambient - (indoor + config.settings.changeoverDelta); } /** - * Get the {@link EconomizerConfig#targetTemperature} delta signal. + * Get the {@link EconomizerSettings#targetTemperature} delta signal. * * @return Positive value indicates demand, negative indicates lack thereof, regardless of mode. */ double getTargetDelta(double indoor) { - return settings.mode == HvacMode.COOLING - ? indoor - settings.targetTemperature - : settings.targetTemperature - indoor; + return config.mode == HvacMode.COOLING + ? indoor - config.settings.targetTemperature + : config.settings.targetTemperature - indoor; } /** @@ -415,7 +423,7 @@ public Signal computeHvacSuppression(SignalVadim Tkachenko 2001-2024 */ -public class EconomizerConfig extends EconomizerSettings { +public class EconomizerConfig { /** * Which mode this device is active in. This mode may be different from the zone mode; it is the user's @@ -21,35 +21,23 @@ public class EconomizerConfig extends EconomizerSettings { public final Double saturationLimit; - /** - * All except {@code enabled} argument constructor (defaults to {@code true}, for common sense. - * - * @param keepHvacOn See {@link #keepHvacOn}. Think twice before setting this to {@code true}. - * @param P Internal {@link net.sf.dz3r.controller.pid.PidController} P component. - * @param I Internal {@link net.sf.dz3r.controller.pid.PidController} I component. - * @param saturationLimit Internal {@link net.sf.dz3r.controller.pid.PidController} saturation limit. - */ - public EconomizerConfig(HvacMode mode, - Double changeoverDelta, Double targetTemperature, - Boolean keepHvacOn, - Double P, Double I, Double saturationLimit) { - this(mode, true, changeoverDelta, targetTemperature, keepHvacOn, P, I, saturationLimit); - } + public final EconomizerSettings settings; /** * All argument constructor. * - * @param keepHvacOn See {@link #keepHvacOn}. Think twice before setting this to {@code true}. * @param P Internal {@link net.sf.dz3r.controller.pid.PidController} P component. * @param I Internal {@link net.sf.dz3r.controller.pid.PidController} I component. * @param saturationLimit Internal {@link net.sf.dz3r.controller.pid.PidController} saturation limit. + * @param settings User changeable settings. */ public EconomizerConfig(HvacMode mode, - Boolean enabled, - Double changeoverDelta, Double targetTemperature, - Boolean keepHvacOn, - Double P, Double I, Double saturationLimit) { - super(enabled, changeoverDelta, targetTemperature, keepHvacOn); + Double P, Double I, Double saturationLimit, + EconomizerSettings settings) { + + if (mode == null) { + throw new IllegalArgumentException("mode can't be null"); + } this.mode = mode; @@ -57,18 +45,7 @@ public EconomizerConfig(HvacMode mode, this.I = I; this.saturationLimit = saturationLimit; - checkArgs(); - } - - protected void checkArgs() { - - if (mode == null) { - throw new IllegalArgumentException("mode can't be null"); - } - - if (changeoverDelta < 0) { - throw new IllegalArgumentException("changeoverDelta must be non-negative"); - } + this.settings = settings; } /** @@ -79,26 +56,25 @@ protected void checkArgs() { public EconomizerConfig merge(EconomizerConfig from) { return new EconomizerConfig( - from.mode != null ? from.mode : mode, - from.enabled != null ? from.enabled : enabled, - from.changeoverDelta != null ? from.changeoverDelta : changeoverDelta, - from.targetTemperature != null ? from.targetTemperature : targetTemperature, - from.keepHvacOn != null ? from.keepHvacOn : keepHvacOn, + from.mode, from.P != null ? from.P : P, from.I != null ? from.I : I, - from.saturationLimit != null ? from.saturationLimit : saturationLimit); + from.saturationLimit != null ? from.saturationLimit : saturationLimit, + from.settings != null ? from.settings : settings); } @Override public String toString() { return "{mode=" + mode - + ", enabled=" + enabled - + ", changeoverDelta=" + changeoverDelta - + ", targetTemperature=" + targetTemperature - + ", keepHvacOn=" + keepHvacOn + + ", enabled=" + isEnabled() + ", P=" + P + ", I=" + I + ", saturationLimit=" + saturationLimit + + ", settings=" + settings + "}"; } + + public boolean isEnabled() { + return settings != null; + } } diff --git a/modules/hcc-model/src/main/java/net/sf/dz3r/device/actuator/economizer/EconomizerSettings.java b/modules/hcc-model/src/main/java/net/sf/dz3r/device/actuator/economizer/EconomizerSettings.java index a37826402..f20b7174f 100644 --- a/modules/hcc-model/src/main/java/net/sf/dz3r/device/actuator/economizer/EconomizerSettings.java +++ b/modules/hcc-model/src/main/java/net/sf/dz3r/device/actuator/economizer/EconomizerSettings.java @@ -7,8 +7,6 @@ */ public class EconomizerSettings { - public final Boolean enabled; - /** * Temperature difference between indoor and outdoor temperature necessary to turn the device on. */ @@ -26,17 +24,19 @@ public class EconomizerSettings { */ public final Boolean keepHvacOn; - public EconomizerSettings(Boolean enabled, Double changeoverDelta, Double targetTemperature, Boolean keepHvacOn) { + public EconomizerSettings(Double changeoverDelta, Double targetTemperature, Boolean keepHvacOn) { + + if (changeoverDelta < 0) { + throw new IllegalArgumentException("changeoverDelta must be non-negative"); + } - this.enabled = enabled; this.changeoverDelta = changeoverDelta; this.targetTemperature = targetTemperature; this.keepHvacOn = keepHvacOn; } - public EconomizerSettings(EconomizerConfig source) { + public EconomizerSettings(EconomizerSettings source) { - this.enabled = source.enabled; this.changeoverDelta = source.changeoverDelta; this.targetTemperature = source.targetTemperature; this.keepHvacOn = source.keepHvacOn; @@ -44,8 +44,7 @@ public EconomizerSettings(EconomizerConfig source) { @Override public String toString() { - return "{enabled=" + enabled - + ", changeoverDelta=" + changeoverDelta + return "{changeoverDelta=" + changeoverDelta + ", targetTemperature=" + targetTemperature + ", keepHvacOn=" + keepHvacOn + "}"; diff --git a/modules/hcc-model/src/test/java/net/sf/dz3r/device/actuator/economizer/AbstractEconomizerTest.java b/modules/hcc-model/src/test/java/net/sf/dz3r/device/actuator/economizer/AbstractEconomizerTest.java index 20d16421e..7748cd36d 100644 --- a/modules/hcc-model/src/test/java/net/sf/dz3r/device/actuator/economizer/AbstractEconomizerTest.java +++ b/modules/hcc-model/src/test/java/net/sf/dz3r/device/actuator/economizer/AbstractEconomizerTest.java @@ -19,22 +19,25 @@ class AbstractEconomizerTest { /** - * Make sure that control signal is computed properly as the indoor temperature is approaching the {@link EconomizerConfig#targetTemperature}. + * Make sure that control signal is computed properly as the indoor temperature is approaching the {@link EconomizerSettings#targetTemperature}. */ @ParameterizedTest @MethodSource("targetAdjustmentProvider") void targetAdjustmentTest(TargetAdjustmentTestData source) { - var settings = new EconomizerConfig( + var config = new EconomizerConfig( source.mode, - source.changeoverDelta, - source.targetTemperature, - true, - 1.0, 0.0001, 1.0); + 1.0, 0.0001, 1.0, + new EconomizerSettings( + source.changeoverDelta, + source.targetTemperature, + true + ) + ); var e = new TestEconomizer( "eco", - settings, + config, new SwitchableHvacDevice( Clock.systemUTC(), "d", diff --git a/modules/hcc-model/src/test/java/net/sf/dz3r/device/actuator/economizer/v1/SimpleEconomizerTest.java b/modules/hcc-model/src/test/java/net/sf/dz3r/device/actuator/economizer/v1/SimpleEconomizerTest.java index 423aefe52..c3615b7fd 100644 --- a/modules/hcc-model/src/test/java/net/sf/dz3r/device/actuator/economizer/v1/SimpleEconomizerTest.java +++ b/modules/hcc-model/src/test/java/net/sf/dz3r/device/actuator/economizer/v1/SimpleEconomizerTest.java @@ -3,6 +3,7 @@ import net.sf.dz3r.device.actuator.NullCqrsSwitch; import net.sf.dz3r.device.actuator.SwitchableHvacDevice; import net.sf.dz3r.device.actuator.economizer.EconomizerConfig; +import net.sf.dz3r.device.actuator.economizer.EconomizerSettings; import net.sf.dz3r.model.HvacMode; import net.sf.dz3r.signal.Signal; import org.apache.logging.log4j.LogManager; @@ -37,13 +38,14 @@ void dropToOnAndBack() { // Target temperature is below the lowest in the ambient flux, // the economizer will just turn on and off - var settings = new EconomizerConfig( + var config = new EconomizerConfig( HvacMode.COOLING, - true, - 2.0, - 10.0, - false, - 1.0, 0.000004, 1.1); + 1.0, 0.000004, 1.1, + new EconomizerSettings( + 2.0, + 10.0, + false + )); var indoor = 25.0; @@ -63,7 +65,7 @@ void dropToOnAndBack() { var economizer = new SimpleEconomizer<>( "economizer", - settings, + config, deferredAmbientFlux, device, Duration.ofSeconds(90)); @@ -91,13 +93,14 @@ void dropToBelowTargetAndBack() { // Target temperature is within the range of the ambient flux, // the economizer will turn on, then off, then on and off again - var settings = new EconomizerConfig( + var config = new EconomizerConfig( HvacMode.COOLING, - true, - 2.0, - 18.0, - false, - 1.0, 0.000004, 1.1); + 1.0, 0.000004, 1.1, + new EconomizerSettings( + 2.0, + 18.0, + false + )); var ambient = getAmbientFlux(); }