Skip to content

Commit

Permalink
Augmented ZoneStatus with EconomizerStatus (#239)
Browse files Browse the repository at this point in the history
  • Loading branch information
climategadgets committed Oct 21, 2022
1 parent 442f780 commit 3baddd2
Show file tree
Hide file tree
Showing 19 changed files with 153 additions and 103 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ private Flux<ZoneSnapshot> convert(Signal<ZoneStatus, String> source) {
source.timestamp.toEpochMilli(),
zoneName,
modeMap.get(hvacMode),
renderState(status.settings.enabled, status.thermostatStatus.calling),
status.thermostatStatus.demand,
renderState(status.settings.enabled, status.callingStatus.calling),
status.callingStatus.demand,
sensorSignal.getValue(),
status.settings.setpoint,
status.settings.enabled,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ private Point convert(Signal<ZoneStatus, String> signal) {
b.addField("hold", status.settings.hold);
b.addField("dumpPriority", status.settings.dumpPriority);

b.addField("calling", status.thermostatStatus.calling);
b.addField("demand", status.thermostatStatus.demand);
b.addField("calling", status.callingStatus.calling);
b.addField("demand", status.callingStatus.demand);
}

if (signal.error != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ protected Map<Damper<?>, Double> compute(Map<String, Signal<ZoneStatus, String>>
}

// Negative demand counts as 0, otherwise damper positions will go below 0 and go boom
var demand = Math.max(0.0, zoneSignal.getValue().thermostatStatus.demand);
var demand = Math.max(0.0, zoneSignal.getValue().callingStatus.demand);
var zoneSet = demand2zone.computeIfAbsent(demand, k -> new TreeSet<>());

zoneSet.add(zoneName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ protected Map<Damper<?>, Double> compute(Map<String, Signal<ZoneStatus, String>>
zone2signal.forEach((key, value) -> {

var damper = getDamperFor(key);
var position = value.getValue().thermostatStatus.calling ? 1d : 0d;
var position = value.getValue().callingStatus.calling ? 1d : 0d;

result.put(damper, position);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
import net.sf.dz3r.model.Zone;
import net.sf.dz3r.signal.Signal;
import net.sf.dz3r.signal.SignalProcessor;
import net.sf.dz3r.signal.hvac.ThermostatStatus;
import net.sf.dz3r.signal.hvac.CallingStatus;
import net.sf.dz3r.signal.hvac.EconomizerStatus;
import net.sf.dz3r.signal.hvac.ZoneStatus;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
Expand Down Expand Up @@ -43,6 +44,13 @@ public abstract class AbstractEconomizer <A extends Comparable<A>> implements Si
*/
private Signal<Double, Void> ambient;

/**
* Economizer status.
*
* Can't be {@code null}, that will throw off {@link Zone#compute(Flux)} reporting.
*/
private EconomizerStatus economizerStatus;

/**
* Mirrors the state of {@link #targetDevice} to avoid expensive operations.
*/
Expand All @@ -66,6 +74,7 @@ protected AbstractEconomizer(

this.settings = settings;
this.targetDevice = targetDevice;
this.economizerStatus = new EconomizerStatus(settings, 0, false, null);

// Don't forget to connect fluxes; this can only be done in subclasses after all the
// necessary components were initialized
Expand Down Expand Up @@ -129,6 +138,8 @@ private void connectCombined(FluxSink<Pair<Signal<Double, String>, Signal<Double

private Boolean recordDeviceState(Signal<Boolean, ProcessController.Status<Double>> stateSignal) {

economizerStatus = new EconomizerStatus(settings, stateSignal.payload.signal, stateSignal.getValue(), ambient);

var newState = stateSignal.getValue();

if ((actuatorState == null && newState != null) || newState.compareTo(actuatorState) != 0) {
Expand Down Expand Up @@ -237,35 +248,47 @@ private Signal<Double, Void> computeCombined(Pair<Signal<Double, String>, Signal
* Figure out whether the HVAC needs to be suppressed and adjust the signal if so.
*
* @param source Signal computed by {@link Zone}.
* @return Signal with {@link ZoneStatus#thermostatStatus} possibly adjusted to shut off the HVAC if the economizer is active.
* @return Signal with {@link ZoneStatus#callingStatus} possibly adjusted to shut off the HVAC if the economizer is active.
*/
public Signal<ZoneStatus, String> computeHvacSuppression(Signal<ZoneStatus, String> source) {

if (actuatorState == null || actuatorState.equals(Boolean.FALSE)) {
if (source.isError()) {

// Economizer inactive, no change required
// How did it get here? This log message is likely a dupe, need to keep an eye on it
logger.warn("error signal in economizer pipeline? {}", source);

// Can't do anything meaningful with it here
return source;
}

if (settings.keepHvacOn) {
// Augment the source with the economizer status
var augmentedSource = new Signal<>(
source.timestamp,
new ZoneStatus(
source.getValue().settings,
source.getValue().callingStatus,
economizerStatus),
source.payload,
source.status,
source.error);

// We're feeding indoor air to HVAC air return, right?
return source;
}
if (actuatorState == null || actuatorState.equals(Boolean.FALSE)) {

if (source.isError()) {
// Economizer inactive, no change required
return augmentedSource;
}

// How did it get here? This log message is likely a dupe, need to keep an eye on it
logger.warn("error signal in economizer pipeline? {}", source);
if (settings.keepHvacOn) {

// Can't do anything meaningful with it here
return source;
// We're feeding indoor air to HVAC air return, right?
return augmentedSource;
}

// Need to suppress demand and keep the HVAC off while the economizer is on
var adjusted = new ZoneStatus(
source.getValue().settings,
new ThermostatStatus(0, false));
new CallingStatus(0, false),
economizerStatus);

return new Signal<>(
source.timestamp,
Expand All @@ -279,10 +302,10 @@ public Signal<ZoneStatus, String> computeHvacSuppression(Signal<ZoneStatus, Stri
public void close() throws Exception {
ThreadContext.push("close");
try {
logger.info("Shutting down");
logger.info("Shutting down: {}", getAddress());
targetDevice.setState(false).block();
} finally {
logger.info("Shut down");
logger.info("Shut down: {}", getAddress());
ThreadContext.pop();
}
}
Expand Down
10 changes: 5 additions & 5 deletions dz3r-model/src/main/java/net/sf/dz3r/model/Thermostat.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import net.sf.dz3r.controller.pid.SimplePidController;
import net.sf.dz3r.device.Addressable;
import net.sf.dz3r.signal.Signal;
import net.sf.dz3r.signal.hvac.ThermostatStatus;
import net.sf.dz3r.signal.hvac.CallingStatus;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import reactor.core.publisher.Flux;
Expand All @@ -28,7 +28,7 @@
*
* @author Copyright &copy; <a href="mailto:[email protected]">Vadim Tkachenko</a> 2001-2021
*/
public class Thermostat implements ProcessController<Double, ThermostatStatus, Void>, Addressable<String> {
public class Thermostat implements ProcessController<Double, CallingStatus, Void>, Addressable<String> {

private final Logger logger = LogManager.getLogger();

Expand Down Expand Up @@ -147,7 +147,7 @@ public double getError() {
* @see net.sf.dz3r.device.actuator.economizer.v2.PidEconomizer#computeDeviceState(Flux)
*/
@Override
public Flux<Signal<Status<ThermostatStatus>, Void>> compute(Flux<Signal<Double, Void>> pv) {
public Flux<Signal<Status<CallingStatus>, Void>> compute(Flux<Signal<Double, Void>> pv) {

logger.debug("compute()");

Expand All @@ -171,14 +171,14 @@ public Flux<Signal<Status<ThermostatStatus>, Void>> compute(Flux<Signal<Double,
.map(this::mapOutput);
}

private Signal<Status<ThermostatStatus>, Void> mapOutput(Signal<Status<Double>, Status<Double>> source) {
private Signal<Status<CallingStatus>, Void> mapOutput(Signal<Status<Double>, Status<Double>> source) {

var demand = source.payload.signal - signalRenderer.getThresholdLow();
var calling = Double.compare(source.getValue().signal, 1.0) == 0;

return new Signal<>(
source.timestamp,
new Status<>(source.payload.setpoint, source.payload.error, new ThermostatStatus(demand, calling)),
new Status<>(source.payload.setpoint, source.payload.error, new CallingStatus(demand, calling)),
null,
source.status,
source.error);
Expand Down
12 changes: 6 additions & 6 deletions dz3r-model/src/main/java/net/sf/dz3r/model/Zone.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import net.sf.dz3r.device.actuator.economizer.v2.PidEconomizer;
import net.sf.dz3r.signal.Signal;
import net.sf.dz3r.signal.SignalProcessor;
import net.sf.dz3r.signal.hvac.ThermostatStatus;
import net.sf.dz3r.signal.hvac.CallingStatus;
import net.sf.dz3r.signal.hvac.ZoneStatus;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
Expand Down Expand Up @@ -121,11 +121,11 @@ public Flux<Signal<ZoneStatus, String>> compute(Flux<Signal<Double, String>> in)
return stage3.map(this::suppressEconomizer);
}

private Signal<ZoneStatus, String> translate(Signal<ProcessController.Status<ThermostatStatus>, Void> source) {
private Signal<ZoneStatus, String> translate(Signal<ProcessController.Status<CallingStatus>, Void> source) {

return new Signal<>(
source.timestamp,
new ZoneStatus(settings, source.getValue().signal),
new ZoneStatus(settings, source.getValue().signal, null),
getAddress(),
source.status,
source.error);
Expand All @@ -139,7 +139,7 @@ private Signal<ZoneStatus, String> suppressIfNotEnabled(Signal<ZoneStatus, Strin

return new Signal<>(
source.timestamp,
new ZoneStatus(new ZoneSettings(source.getValue().settings, false), new ThermostatStatus(0, false)),
new ZoneStatus(new ZoneSettings(source.getValue().settings, false), new CallingStatus(0, false), null),
source.payload,
source.status,
source.error);
Expand Down Expand Up @@ -181,14 +181,14 @@ public void close() throws Exception {
ThreadContext.push("close");
try {

logger.info("Shutting down");
logger.info("Shutting down: {}", getAddress());

if (economizer != null) {
economizer.close();

}
} finally {
logger.info("Shut down");
logger.info("Shut down: {}", getAddress());
ThreadContext.pop();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ private Signal<UnitControlSignal, String> process(Signal<ZoneStatus, String> sig
.filter(kv -> kv.getValue().getValue().settings.enabled);

var unhappy = enabled
.filter(kv -> kv.getValue().getValue().thermostatStatus.calling)
.filter(kv -> kv.getValue().getValue().callingStatus.calling)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

var unhappyVoting = unhappy
Expand Down Expand Up @@ -212,7 +212,7 @@ private double computeDemand(Map<String, Signal<ZoneStatus, String>> source) {
return source
.values()
.stream()
.map(e -> e.getValue().thermostatStatus.demand)
.map(e -> e.getValue().callingStatus.demand)
.reduce(Double::sum).orElse(0d);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package net.sf.dz3r.signal.hvac;

/**
* Calling status.
*
* This object defines the actual thermostat or economizer status in real time.
*
* @author Copyright &copy; <a href="mailto:[email protected]">Vadim Tkachenko</a> 2001-2022
*/
public class CallingStatus {

public final double demand;
public final boolean calling;

public CallingStatus(double demand, boolean calling) {
this.demand = demand;
this.calling = calling;
}

@Override
public String toString() {
return "{demand=" + demand + ", calling=" + calling + "}";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package net.sf.dz3r.signal.hvac;

import net.sf.dz3r.device.actuator.economizer.EconomizerSettings;
import net.sf.dz3r.signal.Signal;

/**
* Economiser status.
*
* @see net.sf.dz3r.device.actuator.economizer.AbstractEconomizer
* @author Copyright &copy; <a href="mailto:[email protected]">Vadim Tkachenko</a> 2001-2022
*/
public class EconomizerStatus {

public final EconomizerSettings settings;
public final CallingStatus callingStatus;
public final Signal<Double, Void> ambient;

public EconomizerStatus(EconomizerSettings settings, double demand, boolean calling, Signal<Double, Void> ambient) {

this.settings = settings;
this.callingStatus = new CallingStatus(demand, calling);
this.ambient = ambient;
}

@Override
public String toString() {
return "{demand=" + callingStatus.demand + ", calling=" + callingStatus.calling + ", ambient=" + ambient + ", settings=" + settings + "}";
}
}

This file was deleted.

12 changes: 7 additions & 5 deletions dz3r-model/src/main/java/net/sf/dz3r/signal/hvac/ZoneStatus.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,22 @@
*
* @see net.sf.dz3r.model.ZoneSettings
*
* @author Copyright &copy; <a href="mailto:[email protected]">Vadim Tkachenko</a> 2001-2021
* @author Copyright &copy; <a href="mailto:[email protected]">Vadim Tkachenko</a> 2001-2022
*/
public class ZoneStatus {

public final ZoneSettings settings;
public final ThermostatStatus thermostatStatus;
public final CallingStatus callingStatus;
public final EconomizerStatus economizerStatus;

public ZoneStatus(ZoneSettings settings, ThermostatStatus thermostatStatus) {
public ZoneStatus(ZoneSettings settings, CallingStatus callingStatus, EconomizerStatus economizerStatus) {
this.settings = settings;
this.thermostatStatus = thermostatStatus;
this.callingStatus = callingStatus;
this.economizerStatus = economizerStatus;
}

@Override
public String toString() {
return "{settings=" + settings + ", thermostat=" + thermostatStatus + "}";
return "{settings=" + settings + ", thermostat=" + callingStatus + ", economizer=" + economizerStatus + "}";
}
}
Loading

0 comments on commit 3baddd2

Please sign in to comment.