Skip to content

Commit

Permalink
Replaced inheritance with ownership, handled ripples (#311)
Browse files Browse the repository at this point in the history
  • Loading branch information
climategadgets committed Apr 6, 2024
1 parent cc0cff5 commit 6befbc1
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,15 @@ private Mono<Point> convertEconomizer(Signal<ZoneStatus, String> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public abstract class AbstractEconomizer implements SignalProcessor<Double, Doub

public final String name;

private EconomizerConfig settings;
private EconomizerConfig config;

private final HvacDevice device;

Expand Down Expand Up @@ -78,15 +78,15 @@ public abstract class AbstractEconomizer implements SignalProcessor<Double, Doub
protected AbstractEconomizer(
Clock clock,
String name,
EconomizerConfig settings,
EconomizerConfig config,
HvacDevice device,
Duration timeout) {

checkModes(settings.mode, device);
checkModes(config.mode, device);

this.clock = clock == null ? Clock.systemUTC() : clock;
this.name = name;
setSettings(settings);
setSettings(config);

this.device = HCCObjects.requireNonNull(device, "device can't be null");
this.timeout = HCCObjects.requireNonNull(timeout, "timeout can't be null");
Expand All @@ -101,7 +101,7 @@ protected AbstractEconomizer(
.subscribe(s -> 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
Expand All @@ -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
Expand Down Expand Up @@ -172,7 +172,7 @@ private void setDeviceState(Boolean state) {
var ctl = Boolean.TRUE.equals(state) ? 1.0 : 0.0;
var signal = new Signal<HvacCommand, Void>(
clock.instant(),
new HvacCommand(settings.mode, ctl, ctl)
new HvacCommand(config.mode, ctl, ctl)
);

logger.debug("{}: setDeviceState={}", getAddress(), signal);
Expand Down Expand Up @@ -206,7 +206,7 @@ private Boolean recordDeviceState(Signal<Boolean, ProcessController.Status<Doubl
var demand = stateSignal.payload == null ? 0 : stateSignal.payload.signal;

economizerStatus = new EconomizerStatus(
new EconomizerSettings(settings),
new EconomizerSettings(config.settings),
sample,
demand,
stateSignal.getValue(),
Expand Down Expand Up @@ -269,6 +269,14 @@ private Signal<Double, Void> 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) {
Expand Down Expand Up @@ -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;
Expand All @@ -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;

Expand All @@ -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;
}

/**
Expand Down Expand Up @@ -415,7 +423,7 @@ public Signal<ZoneStatus, String> computeHvacSuppression(Signal<ZoneStatus, Stri
return augmentedSource;
}

if (Boolean.TRUE.equals(settings.keepHvacOn)) {
if (Boolean.TRUE.equals(config.settings.keepHvacOn)) {

// We're feeding indoor air to HVAC air return, right?
return augmentedSource;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*
* @author Copyright &copy; <a href="mailto:[email protected]">Vadim Tkachenko</a> 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
Expand All @@ -21,54 +21,31 @@ 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;

this.P = P;
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;
}

/**
Expand All @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
*/
public class EconomizerSettings {

public final Boolean enabled;

/**
* Temperature difference between indoor and outdoor temperature necessary to turn the device on.
*/
Expand All @@ -26,26 +24,27 @@ 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;
}

@Override
public String toString() {
return "{enabled=" + enabled
+ ", changeoverDelta=" + changeoverDelta
return "{changeoverDelta=" + changeoverDelta
+ ", targetTemperature=" + targetTemperature
+ ", keepHvacOn=" + keepHvacOn
+ "}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading

0 comments on commit 6befbc1

Please sign in to comment.