Skip to content

Commit

Permalink
Added switches to the status dashboard. Visuals are still ugly, but u…
Browse files Browse the repository at this point in the history
…niformly so (#268)
  • Loading branch information
climategadgets committed Aug 5, 2023
1 parent b7a5ef1 commit 08abb9d
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public class InstrumentCluster {
private final Flux<Map.Entry<String, HvacDevice>> hvacDevices;

private final Map<String, SensorStatusProcessor> sensorProcessors = new HashMap<>();
private final Map<String, SwitchStatusProcessor> switchProcessors = new HashMap<>();

/**
* Status accumulator.
Expand Down Expand Up @@ -65,6 +66,15 @@ public InstrumentCluster(
*/
public Flux<Signal<SystemStatus, Void>> getFlux() {

connectSensors();
connectSwitches();

logger.error("FIXME: NOT IMPLEMENTED: getFlux(SystemStatus)");

return statusSink.asFlux();
}

private void connectSensors() {
sensors
.map(kv -> {
var id = kv.getKey();
Expand All @@ -80,7 +90,7 @@ public Flux<Signal<SystemStatus, Void>> getFlux() {
status
.subscribe(s -> {

logger.info("update: id={}, status={}", id, s);
logger.debug("update: id={}, status={}", id, s);

// Update the accumulated status
currentStatus.sensors().put(id, s);
Expand All @@ -93,10 +103,37 @@ public Flux<Signal<SystemStatus, Void>> getFlux() {
});

});
}

logger.error("FIXME: NOT IMPLEMENTED: getFlux(SystemStatus)");
private void connectSwitches() {

return statusSink.asFlux();
switches
.map(kv -> {
var id = kv.getKey();
var p = switchProcessors.computeIfAbsent(id, SwitchStatusProcessor::new);

return new AbstractMap.SimpleEntry<>(id, p.compute(kv.getValue().getFlux()));
})
.subscribe(kv -> {

String id = kv.getKey();
var status = kv.getValue();

status
.subscribe(s -> {

logger.debug("update: id={}, status={}", id, s);

// Update the accumulated status
currentStatus.switches().put(id, s);

// Send an incremental update
var incrementalStatus = createEmptyStatus();
incrementalStatus.switches().put(id, s);

statusSink.tryEmitNext(new Signal<>(Instant.now(), incrementalStatus));
});
});
}

private SystemStatus createEmptyStatus() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package net.sf.dz3r.instrumentation;

import net.sf.dz3r.device.actuator.Switch;
import net.sf.dz3r.signal.Signal;
import net.sf.dz3r.signal.SignalProcessor;
import net.sf.dz3r.signal.health.SwitchStatus;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import reactor.core.publisher.Flux;

import java.util.Optional;

/**
* Consumes individual switch signal, emits switch status.
*
* @author Copyright &copy; <a href="mailto:[email protected]">Vadim Tkachenko 2001-2023
*/
public class SwitchStatusProcessor implements SignalProcessor<Switch.State, SwitchStatus, String> {

private final Logger logger = LogManager.getLogger();

private final String id;

public SwitchStatusProcessor(String id) {
this.id = id;

logger.info("created switch status processor for id={}", id);
}

@Override
public Flux<Signal<SwitchStatus, String>> compute(Flux<Signal<Switch.State, String>> in) {
return in.map(this::compute);
}

private Signal<SwitchStatus, String> compute(Signal<Switch.State, String> source) {

if (source.isError()) {
// Nothing else matters
return new Signal<>(source.timestamp, null, null, source.status, source.error);
}

// VT: FIXME: Pass/fail is the only thing of interest right now
return new Signal<>(source.timestamp, new SwitchStatus(Optional.empty()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
*/
public record SystemStatus(
Map<String, Signal<SensorStatus, Void>> sensors,
Map<String, SwitchStatus> switches,
Map<String, Signal<SwitchStatus, String>> switches,
Map<String, HvacDeviceStatus> hvacDevices,
Map<String, ConnectorStatus> connectors,
Map<String, ConnectorStatus> collectors
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import net.sf.dz3r.instrumentation.InstrumentCluster;
import net.sf.dz3r.signal.Signal;
import net.sf.dz3r.signal.health.SensorStatus;
import net.sf.dz3r.signal.health.SwitchStatus;
import net.sf.dz3r.signal.health.SystemStatus;
import net.sf.dz3r.view.swing.ColorScheme;
import net.sf.dz3r.view.swing.EntityPanel;
Expand Down Expand Up @@ -32,6 +33,7 @@ public class DashboardPanel extends EntityPanel<SystemStatus, Void> {
private final JPanel hvacDevicePanel = new JPanel();

private final Map<String, JPanel> sensors = new TreeMap<>();
private final Map<String, JPanel> switches = new TreeMap<>();

/**
* Status accumulator.
Expand Down Expand Up @@ -67,7 +69,7 @@ private void initGraphics() {
hvacDevicePanel.setBorder(new TitledBorder("HVAC Devices"));

sensorPanel.setLayout(new BoxLayout(sensorPanel, BoxLayout.X_AXIS));
// switchPanel.setLayout(new BoxLayout(switchPanel, BoxLayout.X_AXIS));
switchPanel.setLayout(new BoxLayout(switchPanel, BoxLayout.X_AXIS));
// connectorPanel.setLayout(new BoxLayout(connectorPanel, BoxLayout.X_AXIS));
// collectorPanel.setLayout(new BoxLayout(collectorPanel, BoxLayout.X_AXIS));
// hvacDevicePanel.setLayout(new BoxLayout(hvacDevicePanel, BoxLayout.X_AXIS));
Expand Down Expand Up @@ -122,6 +124,7 @@ protected void update() {
}

renderSensors(signal.getValue().sensors());
renderSwitches(signal.getValue().switches());
}

private void renderSensors(Map<String, Signal<SensorStatus, Void>> source) {
Expand All @@ -131,7 +134,7 @@ private void renderSensors(Map<String, Signal<SensorStatus, Void>> source) {
// There is likely just one entry, but let's be generic

for (var kv: source.entrySet()) {
render(
renderSensor(
sensors.computeIfAbsent(kv.getKey(), k -> createSensorBox(kv.getKey())),
kv.getKey(),
kv.getValue());
Expand All @@ -149,6 +152,31 @@ private void renderSensors(Map<String, Signal<SensorStatus, Void>> source) {
}
}

private void renderSwitches(Map<String, Signal<SwitchStatus, String>> source) {

var currentCount = switches.size();

// There is likely just one entry, but let's be generic

for (var kv: source.entrySet()) {
renderSwitch(
switches.computeIfAbsent(kv.getKey(), k -> createSensorBox(kv.getKey())),
kv.getKey(),
kv.getValue());
}

var newCount = switches.size();

if (newCount != currentCount) {
// Looks like new sensor has just woken up
switchPanel.removeAll();

for (var kv: switches.entrySet()) {
switchPanel.add(kv.getValue());
}
}
}

private JPanel createSensorBox(String id) {

var result = new JPanel();
Expand All @@ -160,11 +188,16 @@ private JPanel createSensorBox(String id) {
return result;
}

private void render(JPanel p, String id, Signal<SensorStatus, Void> signal) {
private void renderError(JPanel p, String id, Throwable t) {

p.setBackground(ColorScheme.offMap.error);
p.setToolTipText("<html>" + id + "<hr>" + t.getClass().getCanonicalName() + "<br>" + t.getMessage() + "</html>");
}

private void renderSensor(JPanel p, String id, Signal<SensorStatus, Void> signal) {

if (signal.isError()) {
p.setBackground(ColorScheme.offMap.error);
p.setToolTipText("<html>" + id + "<hr>" + signal.getError().getClass().getCanonicalName() + "<br>" + signal.getError().getMessage());
renderError(p, id, signal.error);
return;
}

Expand All @@ -173,7 +206,19 @@ private void render(JPanel p, String id, Signal<SensorStatus, Void> signal) {
+ id
+ "<hr>" + Optional.ofNullable(signal.getValue().resolution())
.map(r -> "Resolution: " + r)
.orElse(""));
.orElse("")
+ "</html>");
}

private void renderSwitch(JPanel p, String id, Signal<SwitchStatus, String> signal) {

if (signal.isError()) {
renderError(p, id, signal.error);
return;
}

p.setBackground(ColorScheme.offMap.green);
p.setToolTipText("<html>" + id + "</html>");
}

private void setTitleColor(Color c) {
Expand Down

0 comments on commit 08abb9d

Please sign in to comment.