From 2a824b2fd2a85553effb8a87107673ef819e49ab Mon Sep 17 00:00:00 2001 From: SolarNet Developer Date: Fri, 26 Jan 2018 00:48:23 +0000 Subject: [PATCH 1/3] moved the demand response plugins to a new branch off of devolop in prep for pull request --- .../.classpath | 7 + .../.gitignore | 2 + .../.project | 28 ++ .../.settings/org.eclipse.jdt.core.prefs | 7 + .../.settings/org.eclipse.pde.core.prefs | 4 + .../META-INF/MANIFEST.MF | 31 ++ .../OSGI-INF/blueprint/module.xml | 144 ++++++++ .../build.properties | 4 + .../dresimplestrategy/DRESimpleStrategy.java | 117 +++++++ .../DRESimpleStrategyDatumDataSource.java | 88 +++++ ...RESimpleStrategyDatumDataSource.properties | 13 + .../.classpath | 7 + .../.gitignore | 2 + .../.project | 28 ++ .../.settings/org.eclipse.jdt.core.prefs | 7 + .../.settings/org.eclipse.pde.core.prefs | 4 + .../META-INF/MANIFEST.MF | 31 ++ .../OSGI-INF/blueprint/module.xml | 147 ++++++++ .../build.properties | 4 + .../dretargetcost/DRETargetCost$1.class | Bin 0 -> 1229 bytes .../dretargetcost/DRETargetCost.class | Bin 0 -> 7911 bytes .../DRETargetCostDatumDataSource.class | Bin 0 -> 3929 bytes .../DRETargetCostDatumDataSource.properties | 26 ++ ...rtTools$NotChargeableDeviceException.class | Bin 0 -> 634 bytes .../dretargetcost/DRSupportTools.class | Bin 0 -> 3987 bytes .../dretargetcost/MinimumDRStrategy.class | Bin 0 -> 2919 bytes .../dretargetcost/DRETargetCost.java | 263 ++++++++++++++ .../DRETargetCostDatumDataSource.java | 96 ++++++ .../DRETargetCostDatumDataSource.properties | 26 ++ .../dretargetcost/DRSupportTools.java | 320 ++++++++++++++++++ .../dretargetcost/MinimumDRStrategy.java | 57 ++++ .../.classpath | 7 + .../.gitignore | 2 + .../.project | 28 ++ .../.settings/org.eclipse.jdt.core.prefs | 7 + .../.settings/org.eclipse.pde.core.prefs | 4 + .../META-INF/MANIFEST.MF | 29 ++ .../OSGI-INF/blueprint/module.xml | 137 ++++++++ .../build.properties | 4 + .../demandresponse/mockbattery/DRBattery.java | 204 +++++++++++ .../mockbattery/DRBatteryDatumDataSource.java | 183 ++++++++++ .../DRBatteryDatumDataSource.properties | 33 ++ .../mockbattery/MockBattery.java | 135 ++++++++ .../.classpath | 7 + .../.gitignore | 2 + .../.project | 28 ++ .../.settings/org.eclipse.jdt.core.prefs | 7 + .../.settings/org.eclipse.pde.core.prefs | 3 + .../META-INF/MANIFEST.MF | 29 ++ .../OSGI-INF/blueprint/module.xml | 142 ++++++++ .../MockEnergyMeterDatumSource.properties | 32 ++ .../build.properties | 5 + .../mockdrconsumer/MockDRConsumer.java | 192 +++++++++++ .../MockDRConsumerDatumDataSource.java | 109 ++++++ .../MockDRConsumerDatumDataSource.properties | 15 + .../mockdrconsumer/MockDRDeviceSettings.java | 92 +++++ 56 files changed, 2899 insertions(+) create mode 100644 net.solarnetwork.node.demandresponse.dresimplestrategy/.classpath create mode 100644 net.solarnetwork.node.demandresponse.dresimplestrategy/.gitignore create mode 100644 net.solarnetwork.node.demandresponse.dresimplestrategy/.project create mode 100644 net.solarnetwork.node.demandresponse.dresimplestrategy/.settings/org.eclipse.jdt.core.prefs create mode 100644 net.solarnetwork.node.demandresponse.dresimplestrategy/.settings/org.eclipse.pde.core.prefs create mode 100644 net.solarnetwork.node.demandresponse.dresimplestrategy/META-INF/MANIFEST.MF create mode 100644 net.solarnetwork.node.demandresponse.dresimplestrategy/OSGI-INF/blueprint/module.xml create mode 100644 net.solarnetwork.node.demandresponse.dresimplestrategy/build.properties create mode 100644 net.solarnetwork.node.demandresponse.dresimplestrategy/src/net/solarnetwork/node/demandresponse/dresimplestrategy/DRESimpleStrategy.java create mode 100644 net.solarnetwork.node.demandresponse.dresimplestrategy/src/net/solarnetwork/node/demandresponse/dresimplestrategy/DRESimpleStrategyDatumDataSource.java create mode 100644 net.solarnetwork.node.demandresponse.dresimplestrategy/src/net/solarnetwork/node/demandresponse/dresimplestrategy/DRESimpleStrategyDatumDataSource.properties create mode 100644 net.solarnetwork.node.demandresponse.dretargetcost/.classpath create mode 100644 net.solarnetwork.node.demandresponse.dretargetcost/.gitignore create mode 100644 net.solarnetwork.node.demandresponse.dretargetcost/.project create mode 100644 net.solarnetwork.node.demandresponse.dretargetcost/.settings/org.eclipse.jdt.core.prefs create mode 100644 net.solarnetwork.node.demandresponse.dretargetcost/.settings/org.eclipse.pde.core.prefs create mode 100644 net.solarnetwork.node.demandresponse.dretargetcost/META-INF/MANIFEST.MF create mode 100644 net.solarnetwork.node.demandresponse.dretargetcost/OSGI-INF/blueprint/module.xml create mode 100644 net.solarnetwork.node.demandresponse.dretargetcost/build.properties create mode 100644 net.solarnetwork.node.demandresponse.dretargetcost/build/eclipse/net/solarnetwork/node/demandresponse/dretargetcost/DRETargetCost$1.class create mode 100644 net.solarnetwork.node.demandresponse.dretargetcost/build/eclipse/net/solarnetwork/node/demandresponse/dretargetcost/DRETargetCost.class create mode 100644 net.solarnetwork.node.demandresponse.dretargetcost/build/eclipse/net/solarnetwork/node/demandresponse/dretargetcost/DRETargetCostDatumDataSource.class create mode 100644 net.solarnetwork.node.demandresponse.dretargetcost/build/eclipse/net/solarnetwork/node/demandresponse/dretargetcost/DRETargetCostDatumDataSource.properties create mode 100644 net.solarnetwork.node.demandresponse.dretargetcost/build/eclipse/net/solarnetwork/node/demandresponse/dretargetcost/DRSupportTools$NotChargeableDeviceException.class create mode 100644 net.solarnetwork.node.demandresponse.dretargetcost/build/eclipse/net/solarnetwork/node/demandresponse/dretargetcost/DRSupportTools.class create mode 100644 net.solarnetwork.node.demandresponse.dretargetcost/build/eclipse/net/solarnetwork/node/demandresponse/dretargetcost/MinimumDRStrategy.class create mode 100644 net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/DRETargetCost.java create mode 100644 net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/DRETargetCostDatumDataSource.java create mode 100644 net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/DRETargetCostDatumDataSource.properties create mode 100644 net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/DRSupportTools.java create mode 100644 net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/MinimumDRStrategy.java create mode 100644 net.solarnetwork.node.demandresponse.mockbattery/.classpath create mode 100644 net.solarnetwork.node.demandresponse.mockbattery/.gitignore create mode 100644 net.solarnetwork.node.demandresponse.mockbattery/.project create mode 100644 net.solarnetwork.node.demandresponse.mockbattery/.settings/org.eclipse.jdt.core.prefs create mode 100644 net.solarnetwork.node.demandresponse.mockbattery/.settings/org.eclipse.pde.core.prefs create mode 100644 net.solarnetwork.node.demandresponse.mockbattery/META-INF/MANIFEST.MF create mode 100644 net.solarnetwork.node.demandresponse.mockbattery/OSGI-INF/blueprint/module.xml create mode 100644 net.solarnetwork.node.demandresponse.mockbattery/build.properties create mode 100644 net.solarnetwork.node.demandresponse.mockbattery/src/net/solarnetwork/node/demandresponse/mockbattery/DRBattery.java create mode 100644 net.solarnetwork.node.demandresponse.mockbattery/src/net/solarnetwork/node/demandresponse/mockbattery/DRBatteryDatumDataSource.java create mode 100644 net.solarnetwork.node.demandresponse.mockbattery/src/net/solarnetwork/node/demandresponse/mockbattery/DRBatteryDatumDataSource.properties create mode 100644 net.solarnetwork.node.demandresponse.mockbattery/src/net/solarnetwork/node/demandresponse/mockbattery/MockBattery.java create mode 100644 net.solarnetwork.node.demandresponse.mockdrconsumer/.classpath create mode 100644 net.solarnetwork.node.demandresponse.mockdrconsumer/.gitignore create mode 100644 net.solarnetwork.node.demandresponse.mockdrconsumer/.project create mode 100644 net.solarnetwork.node.demandresponse.mockdrconsumer/.settings/org.eclipse.jdt.core.prefs create mode 100644 net.solarnetwork.node.demandresponse.mockdrconsumer/.settings/org.eclipse.pde.core.prefs create mode 100644 net.solarnetwork.node.demandresponse.mockdrconsumer/META-INF/MANIFEST.MF create mode 100644 net.solarnetwork.node.demandresponse.mockdrconsumer/OSGI-INF/blueprint/module.xml create mode 100644 net.solarnetwork.node.demandresponse.mockdrconsumer/bin/build/eclipse/net/solarnetwork/node/datum/energymeter/mock/MockEnergyMeterDatumSource.properties create mode 100644 net.solarnetwork.node.demandresponse.mockdrconsumer/build.properties create mode 100644 net.solarnetwork.node.demandresponse.mockdrconsumer/src/net/solarnetwork/node/demandresponse/mockdrconsumer/MockDRConsumer.java create mode 100644 net.solarnetwork.node.demandresponse.mockdrconsumer/src/net/solarnetwork/node/demandresponse/mockdrconsumer/MockDRConsumerDatumDataSource.java create mode 100644 net.solarnetwork.node.demandresponse.mockdrconsumer/src/net/solarnetwork/node/demandresponse/mockdrconsumer/MockDRConsumerDatumDataSource.properties create mode 100644 net.solarnetwork.node.demandresponse.mockdrconsumer/src/net/solarnetwork/node/demandresponse/mockdrconsumer/MockDRDeviceSettings.java diff --git a/net.solarnetwork.node.demandresponse.dresimplestrategy/.classpath b/net.solarnetwork.node.demandresponse.dresimplestrategy/.classpath new file mode 100644 index 000000000..2374a41aa --- /dev/null +++ b/net.solarnetwork.node.demandresponse.dresimplestrategy/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/net.solarnetwork.node.demandresponse.dresimplestrategy/.gitignore b/net.solarnetwork.node.demandresponse.dresimplestrategy/.gitignore new file mode 100644 index 000000000..c3dca1b96 --- /dev/null +++ b/net.solarnetwork.node.demandresponse.dresimplestrategy/.gitignore @@ -0,0 +1,2 @@ +/build +/target diff --git a/net.solarnetwork.node.demandresponse.dresimplestrategy/.project b/net.solarnetwork.node.demandresponse.dresimplestrategy/.project new file mode 100644 index 000000000..b68fcb300 --- /dev/null +++ b/net.solarnetwork.node.demandresponse.dresimplestrategy/.project @@ -0,0 +1,28 @@ + + + net.solarnetwork.node.demandresponse.dresimplestrategy + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/net.solarnetwork.node.demandresponse.dresimplestrategy/.settings/org.eclipse.jdt.core.prefs b/net.solarnetwork.node.demandresponse.dresimplestrategy/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..c537b6306 --- /dev/null +++ b/net.solarnetwork.node.demandresponse.dresimplestrategy/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/net.solarnetwork.node.demandresponse.dresimplestrategy/.settings/org.eclipse.pde.core.prefs b/net.solarnetwork.node.demandresponse.dresimplestrategy/.settings/org.eclipse.pde.core.prefs new file mode 100644 index 000000000..e8ff8be0b --- /dev/null +++ b/net.solarnetwork.node.demandresponse.dresimplestrategy/.settings/org.eclipse.pde.core.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +pluginProject.equinox=false +pluginProject.extensions=false +resolve.requirebundle=false diff --git a/net.solarnetwork.node.demandresponse.dresimplestrategy/META-INF/MANIFEST.MF b/net.solarnetwork.node.demandresponse.dresimplestrategy/META-INF/MANIFEST.MF new file mode 100644 index 000000000..f88d5bcc1 --- /dev/null +++ b/net.solarnetwork.node.demandresponse.dresimplestrategy/META-INF/MANIFEST.MF @@ -0,0 +1,31 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Mimumum DR Engine +Bundle-SymbolicName: net.solarnetwork.node.demandresponse.dresimplestrategy +Bundle-Version: 1.0.0 +Bundle-Vendor: SolarNetwork +Bundle-RequiredExecutionEnvironment: JavaSE-1.6 +Import-Package: build.eclipse.net.solarnetwork.node.demandresponse.dretargetcost, + net.solarnetwork.node;version="1.23.0", + net.solarnetwork.node.dao;version="1.8.0", + net.solarnetwork.node.demandresponse.dretargetcost, + net.solarnetwork.node.domain;version="1.11.0", + net.solarnetwork.node.job;version="1.13.4", + net.solarnetwork.node.reactor;version="1.3.0", + net.solarnetwork.node.reactor.support;version="1.3.0", + net.solarnetwork.node.settings;version="1.10.0", + net.solarnetwork.node.settings.support;version="1.8.0", + net.solarnetwork.node.support;version="1.14.0", + net.solarnetwork.node.test;version="1.3.0", + net.solarnetwork.node.util;version="1.7.2", + net.solarnetwork.util;version="1.28.0", + org.junit;version="[4.12.0,5.0.0)", + org.junit.runner;version="[4.12.0,5.0.0)", + org.quartz;version="[2.2.3,3.0.0)", + org.quartz.simpl;version="[2.2.3,3.0.0)", + org.slf4j;version="[1.7.24,2.0.0)", + org.springframework.beans;version="[4.2.9.RELEASE,5.0.0)", + org.springframework.context;version="[4.2.9.RELEASE,5.0.0)", + org.springframework.context.support;version="[4.2.9.RELEASE,5.0.0)", + org.springframework.core;version="[4.2.9.RELEASE,5.0.0)", + org.springframework.scheduling.quartz;version="[4.2.9.RELEASE,5.0.0)" diff --git a/net.solarnetwork.node.demandresponse.dresimplestrategy/OSGI-INF/blueprint/module.xml b/net.solarnetwork.node.demandresponse.dresimplestrategy/OSGI-INF/blueprint/module.xml new file mode 100644 index 000000000..a4ec31e00 --- /dev/null +++ b/net.solarnetwork.node.demandresponse.dresimplestrategy/OSGI-INF/blueprint/module.xml @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + net.solarnetwork.node.job.ManagedTriggerAndJobDetail + net.solarnetwork.node.job.ServiceProvider + net.solarnetwork.node.settings.SettingSpecifierProvider + + + + + + + + + + + + + + + + + + + + + + + + net.solarnetwork.node.DatumDataSource + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/net.solarnetwork.node.demandresponse.dresimplestrategy/build.properties b/net.solarnetwork.node.demandresponse.dresimplestrategy/build.properties new file mode 100644 index 000000000..668f1032f --- /dev/null +++ b/net.solarnetwork.node.demandresponse.dresimplestrategy/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = build/eclipse/ +bin.includes = META-INF/,\ + . diff --git a/net.solarnetwork.node.demandresponse.dresimplestrategy/src/net/solarnetwork/node/demandresponse/dresimplestrategy/DRESimpleStrategy.java b/net.solarnetwork.node.demandresponse.dresimplestrategy/src/net/solarnetwork/node/demandresponse/dresimplestrategy/DRESimpleStrategy.java new file mode 100644 index 000000000..8ed66fbf1 --- /dev/null +++ b/net.solarnetwork.node.demandresponse.dresimplestrategy/src/net/solarnetwork/node/demandresponse/dresimplestrategy/DRESimpleStrategy.java @@ -0,0 +1,117 @@ +package net.solarnetwork.node.demandresponse.dresimplestrategy; + +import java.util.Collection; +import java.util.Date; +import java.util.Map; + +import net.solarnetwork.node.demandresponse.dretargetcost.DRSupportTools; +import net.solarnetwork.node.reactor.FeedbackInstructionHandler; +import net.solarnetwork.node.reactor.Instruction; +import net.solarnetwork.node.reactor.InstructionHandler; +import net.solarnetwork.node.reactor.support.BasicInstruction; + +/** + * Expirimental class looking into how a demand responce system my look like. + * Method names and API use will probably change in refactoring into a propper + * implementation. + * + * + * This class is very simple it turns off as many devices as it can and controls + * batterys via a mode textbox + * + * + * @author robert + * + */ +public class DRESimpleStrategy { + private DRESimpleStrategyDatumDataSource settings; + private Collection feedbackInstructionHandlers; + + // status variables for the datum source + private Integer numdrdevices = 0; + + public DRESimpleStrategyDatumDataSource getSettings() { + return settings; + } + + // configured in OSGI + public void setSettings(DRESimpleStrategyDatumDataSource settings) { + this.settings = settings; + + } + + // TODO give this method a massive refactoring + protected void drupdate() { + numdrdevices = 0; + for (FeedbackInstructionHandler handler : feedbackInstructionHandlers) { + if (handler.handlesTopic(DRSupportTools.DRPARAMS_INSTRUCTION)) { + + BasicInstruction instr = new BasicInstruction(DRSupportTools.DRPARAMS_INSTRUCTION, new Date(), + Instruction.LOCAL_INSTRUCTION_ID, Instruction.LOCAL_INSTRUCTION_ID, null); + + // The devices want to know where the instruction came from for + // verification + instr.addParameter(settings.getUID(), ""); + + Map params = handler.processInstructionWithFeedback(instr).getResultParameters(); + if (DRSupportTools.isDRCapable(params)) { + numdrdevices++; + Integer watts = DRSupportTools.readWatts(params); + Integer minwatts = DRSupportTools.readMinWatts(params); + Integer maxwatts = DRSupportTools.readMaxWatts(params); + if (DRSupportTools.isChargeable(params)) { + String mode = settings.getBatteryMode(); + if (mode.equalsIgnoreCase("DISCHARGE")) { + // Tell the battery to discharge as much as it can + sendDRtoBattery(true, handler, maxwatts); + } else if (mode.equalsIgnoreCase("CHARGE")) { + // Tell the battery to charge as much as it can. For + // that we need the max charging watts param + sendDRtoBattery(false, handler, DRSupportTools.readMaxChargingWatts(params)); + } else { + // Assume idle tell battery to not discharge and set + // draw to 0 watts + // it should be the case for batterys to have + // minwatts at 0 but check anyways + if (minwatts.equals(0)) { + sendDRtoBattery(false, handler, 0); + } + } + } else if (watts > minwatts) { + Integer shedamount = watts - minwatts; + // in this strategy we just lower power of all devices + // as much as we can + instr = new BasicInstruction(InstructionHandler.TOPIC_SHED_LOAD, new Date(), + Instruction.LOCAL_INSTRUCTION_ID, Instruction.LOCAL_INSTRUCTION_ID, null); + + instr.addParameter(settings.getUID(), shedamount.toString()); + handler.processInstruction(instr); + } + } + } + } + + } + + private void sendDRtoBattery(Boolean discharge, FeedbackInstructionHandler handler, Integer wattValue) { + BasicInstruction instr = new BasicInstruction(InstructionHandler.TOPIC_SET_CONTROL_PARAMETER, new Date(), + Instruction.LOCAL_INSTRUCTION_ID, Instruction.LOCAL_INSTRUCTION_ID, null); + instr.addParameter(settings.getUID(), ""); + instr.addParameter(DRSupportTools.WATTS_PARAM, wattValue.toString()); + instr.addParameter(DRSupportTools.DISCHARGING_PARAM, discharge.toString()); + handler.processInstruction(instr); + } + + public Collection getFeedbackInstructionHandlers() { + return feedbackInstructionHandlers; + } + + // configured in OSGI + public void setFeedbackInstructionHandlers(Collection feedbackInstructionHandlers) { + this.feedbackInstructionHandlers = feedbackInstructionHandlers; + } + + public Integer getNumdrdevices() { + return numdrdevices; + } +} diff --git a/net.solarnetwork.node.demandresponse.dresimplestrategy/src/net/solarnetwork/node/demandresponse/dresimplestrategy/DRESimpleStrategyDatumDataSource.java b/net.solarnetwork.node.demandresponse.dresimplestrategy/src/net/solarnetwork/node/demandresponse/dresimplestrategy/DRESimpleStrategyDatumDataSource.java new file mode 100644 index 000000000..bfba7a50f --- /dev/null +++ b/net.solarnetwork.node.demandresponse.dresimplestrategy/src/net/solarnetwork/node/demandresponse/dresimplestrategy/DRESimpleStrategyDatumDataSource.java @@ -0,0 +1,88 @@ +package net.solarnetwork.node.demandresponse.dresimplestrategy; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import net.solarnetwork.node.DatumDataSource; +import net.solarnetwork.node.domain.GeneralNodeEnergyStorageDatum; +import net.solarnetwork.node.settings.SettingSpecifier; +import net.solarnetwork.node.settings.SettingSpecifierProvider; +import net.solarnetwork.node.settings.support.BasicRadioGroupSettingSpecifier; +import net.solarnetwork.node.support.DatumDataSourceSupport; + +public class DRESimpleStrategyDatumDataSource extends DatumDataSourceSupport + implements SettingSpecifierProvider, DatumDataSource { + + private DRESimpleStrategy linkedInstance; + private String batteryMode = "Idle"; + + // TODO refactor how linkinstance works + + @Override + public String getSettingUID() { + return "net.solarnetwork.node.demandresponse.dresimplestrategy"; + } + + @Override + public String getDisplayName() { + return "Minimum DR Engine"; + } + + @Override + public List getSettingSpecifiers() { + DRESimpleStrategyDatumDataSource defaults = new DRESimpleStrategyDatumDataSource(); + List results = getIdentifiableSettingSpecifiers(); + // results.add(new BasicTextFieldSettingSpecifier("batteryMode", + // defaults.batteryMode)); + BasicRadioGroupSettingSpecifier batteryModesSettings = new BasicRadioGroupSettingSpecifier("batteryMode", + batteryMode); + Map batteryModesMap = new LinkedHashMap(3); + batteryModesMap.put("Idle", "Idle"); + batteryModesMap.put("Charge", "Charge"); + batteryModesMap.put("Discharge", "Discharge"); + batteryModesSettings.setValueTitles(batteryModesMap); + results.add(batteryModesSettings); + return results; + } + + public DRESimpleStrategy getLinkedInstance() { + return linkedInstance; + } + + public void setLinkedInstance(DRESimpleStrategy linkedInstance) { + this.linkedInstance = linkedInstance; + linkedInstance.setSettings(this); + } + + @Override + public Class getDatumType() { + return GeneralNodeEnergyStorageDatum.class; + } + + @Override + public GeneralNodeEnergyStorageDatum readCurrentDatum() { + try { + getLinkedInstance().drupdate(); + } catch (RuntimeException e) { + // exceptions don't print inside this method. For debugging purposes + // I print the stacktrace if there is an exception + e.printStackTrace(); + } + + // the datum will contain num devices as well as watts cost? + GeneralNodeEnergyStorageDatum datum = new GeneralNodeEnergyStorageDatum(); + datum.putInstantaneousSampleValue("Num Devices", getLinkedInstance().getNumdrdevices()); + datum.setSourceId(getUID()); + return datum; + } + + public String getBatteryMode() { + return batteryMode; + } + + public void setBatteryMode(String batteryMode) { + this.batteryMode = batteryMode; + } + +} diff --git a/net.solarnetwork.node.demandresponse.dresimplestrategy/src/net/solarnetwork/node/demandresponse/dresimplestrategy/DRESimpleStrategyDatumDataSource.properties b/net.solarnetwork.node.demandresponse.dresimplestrategy/src/net/solarnetwork/node/demandresponse/dresimplestrategy/DRESimpleStrategyDatumDataSource.properties new file mode 100644 index 000000000..270b00e39 --- /dev/null +++ b/net.solarnetwork.node.demandresponse.dresimplestrategy/src/net/solarnetwork/node/demandresponse/dresimplestrategy/DRESimpleStrategyDatumDataSource.properties @@ -0,0 +1,13 @@ +title= Minimum DR Engine +uid.key = UID +groupUID.key = Service Group +groupUID.desc = An optional group to include this service in. +triggerCronExpression.key = Schedule +triggerCronExpression.desc = A \ + \ + cron expression representing the schedule to sample data at. +batteryMode.key = Battery Mode +batteryMode.desc = Possible values Charge, Discharge and Idle. +Idle.desc = Set batteries to Idle +Charge.desc = Tell batteries to start charging +Discharge.desc = Tell batteries to start discharging diff --git a/net.solarnetwork.node.demandresponse.dretargetcost/.classpath b/net.solarnetwork.node.demandresponse.dretargetcost/.classpath new file mode 100644 index 000000000..2374a41aa --- /dev/null +++ b/net.solarnetwork.node.demandresponse.dretargetcost/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/net.solarnetwork.node.demandresponse.dretargetcost/.gitignore b/net.solarnetwork.node.demandresponse.dretargetcost/.gitignore new file mode 100644 index 000000000..c3dca1b96 --- /dev/null +++ b/net.solarnetwork.node.demandresponse.dretargetcost/.gitignore @@ -0,0 +1,2 @@ +/build +/target diff --git a/net.solarnetwork.node.demandresponse.dretargetcost/.project b/net.solarnetwork.node.demandresponse.dretargetcost/.project new file mode 100644 index 000000000..e3c32c3e8 --- /dev/null +++ b/net.solarnetwork.node.demandresponse.dretargetcost/.project @@ -0,0 +1,28 @@ + + + net.solarnetwork.node.demandresponse.dretargetcost + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/net.solarnetwork.node.demandresponse.dretargetcost/.settings/org.eclipse.jdt.core.prefs b/net.solarnetwork.node.demandresponse.dretargetcost/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..c537b6306 --- /dev/null +++ b/net.solarnetwork.node.demandresponse.dretargetcost/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/net.solarnetwork.node.demandresponse.dretargetcost/.settings/org.eclipse.pde.core.prefs b/net.solarnetwork.node.demandresponse.dretargetcost/.settings/org.eclipse.pde.core.prefs new file mode 100644 index 000000000..e8ff8be0b --- /dev/null +++ b/net.solarnetwork.node.demandresponse.dretargetcost/.settings/org.eclipse.pde.core.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +pluginProject.equinox=false +pluginProject.extensions=false +resolve.requirebundle=false diff --git a/net.solarnetwork.node.demandresponse.dretargetcost/META-INF/MANIFEST.MF b/net.solarnetwork.node.demandresponse.dretargetcost/META-INF/MANIFEST.MF new file mode 100644 index 000000000..5c77d2ccd --- /dev/null +++ b/net.solarnetwork.node.demandresponse.dretargetcost/META-INF/MANIFEST.MF @@ -0,0 +1,31 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Mock Anouncer +Bundle-SymbolicName: net.solarnetwork.node.demandresponse.dretargetcost +Bundle-Version: 1.0.0 +Bundle-Vendor: SolarNetwork +Bundle-RequiredExecutionEnvironment: JavaSE-1.6 +Import-Package: net.solarnetwork.node;version="1.23.0", + net.solarnetwork.node.dao;version="1.8.0", + net.solarnetwork.node.domain;version="1.11.0", + net.solarnetwork.node.job;version="1.13.4", + net.solarnetwork.node.reactor;version="1.3.0", + net.solarnetwork.node.reactor.support;version="1.3.0", + net.solarnetwork.node.settings;version="1.10.0", + net.solarnetwork.node.settings.support;version="1.8.0", + net.solarnetwork.node.support;version="1.14.0", + net.solarnetwork.node.test;version="1.3.0", + net.solarnetwork.node.util;version="1.7.2", + net.solarnetwork.util;version="1.28.0", + org.junit;version="[4.12.0,5.0.0)", + org.junit.runner;version="[4.12.0,5.0.0)", + org.quartz;version="[2.2.3,3.0.0)", + org.quartz.simpl;version="[2.2.3,3.0.0)", + org.slf4j;version="[1.7.24,2.0.0)", + org.springframework.beans;version="[4.2.9.RELEASE,5.0.0)", + org.springframework.context;version="[4.2.9.RELEASE,5.0.0)", + org.springframework.context.support;version="[4.2.9.RELEASE,5.0.0)", + org.springframework.core;version="[4.2.9.RELEASE,5.0.0)", + org.springframework.scheduling.quartz;version="[4.2.9.RELEASE,5.0.0)" +Export-Package: build.eclipse.net.solarnetwork.node.demandresponse.dretargetcost, + net.solarnetwork.node.demandresponse.dretargetcost diff --git a/net.solarnetwork.node.demandresponse.dretargetcost/OSGI-INF/blueprint/module.xml b/net.solarnetwork.node.demandresponse.dretargetcost/OSGI-INF/blueprint/module.xml new file mode 100644 index 000000000..8d227f735 --- /dev/null +++ b/net.solarnetwork.node.demandresponse.dretargetcost/OSGI-INF/blueprint/module.xml @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + net.solarnetwork.node.job.ManagedTriggerAndJobDetail + net.solarnetwork.node.job.ServiceProvider + net.solarnetwork.node.settings.SettingSpecifierProvider + + + + + + + + + + + + + + + + + + + + + + + + net.solarnetwork.node.DatumDataSource + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/net.solarnetwork.node.demandresponse.dretargetcost/build.properties b/net.solarnetwork.node.demandresponse.dretargetcost/build.properties new file mode 100644 index 000000000..668f1032f --- /dev/null +++ b/net.solarnetwork.node.demandresponse.dretargetcost/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = build/eclipse/ +bin.includes = META-INF/,\ + . diff --git a/net.solarnetwork.node.demandresponse.dretargetcost/build/eclipse/net/solarnetwork/node/demandresponse/dretargetcost/DRETargetCost$1.class b/net.solarnetwork.node.demandresponse.dretargetcost/build/eclipse/net/solarnetwork/node/demandresponse/dretargetcost/DRETargetCost$1.class new file mode 100644 index 0000000000000000000000000000000000000000..9ae37b1a20965ffc92f49767f0561594ccb04de8 GIT binary patch literal 1229 zcmbtTTTc@~6#k}NT3Q!c3l#B!w<^8CQsRTLNYqjil8T8my!bTjPRinTr9#%eZah4(>9f#;9${uvEMNI|{9r_8DTb#;`DfK)bf+}r~t{?x`2@(k%iIaFP-=b8Rqnz;>KJ>`Dr^#UFZ74_uh zb;Ob7%J6MZC}W4Zn`s7JUxr@Ldn=-2=_awx@M_xWwBhKfnRV50;PQy_wD*HRsD{tO zP=t6u^~Pz z>qGy!;}C$Q;>89ufiu&N8_Q=>c8)LGGr6HyI+Ji>31`?&Cvr|cn@Q*SnR8q_H{iJO zOx}%kZ91z@eRcB7K$SrK#r8Hkma@|Wu?_tfJ8@UQ%sZ}|Ob_G*&gnVYMY?RaFw7sj zH&e*PoeqH)UF0~4emg$YozAa{H>>dTE;3iYC&P*yrMC|9Zn%?9< zn!<99z^00W&X@*l&avZeCKp>X?YbQ+xqy`}3@364XInDv5Xek#4aH1%+I0q;90!HY zNT!qSN&&sOWwSt4Cn*m@M}vhKs10K#nheYmXeiU_Pqr`{a|DcScBUxrCXMJI~-^uma{VDFeu_qI^Q=9EvQhxi5RqkMth*wXu|0A~uZZJYq z=`HEMx4Gr0g-*5rxxs}NsuAL9G_~?-ea-*(#iWwVM6Qrc*sf#XMC!F{U#oICduLBF z?}l*#8Vs})^`@+Et(_lSXJ^A`MuWt>#DW1cf|GHIftbLovY@h|S~!&s3Nz_CIh#f& z)$D6o3GMdVJ0ds@ryF=FRa%x)?6t5A%V}L{X9xXpR#8I0OFIm-3oNhforgDg6K9YK zs=8}amoj+ii*`EhM6d!YExa6O(jf+wSLXXN*<@UxsX1V+y>5;XqNC+J1FHp2e|{YK zLN=Spxv|xDJ{b>qy@4))+Vb{kDRibzG?;jWKy}ZC&Q(2OtU|YRu2%|NS^+^I63FQt zQ!!R(d49M7guN~!PQIgMa~M5XZ(yCkhRTzV+{q^SgM|&)$iPBnoZC%4CTiQ~*~BIR zNG|#;oC_MYWhW9F?VLUA(4quRnwA=6G(~l7mim2_g{?SGV1722VW`Uo_PZtN4i>|t zzy;I!W6{6@GdrdW6cxt$tVZx^Twvfe0IyDx{Go+ zdT>V={YV&~4X>)a&|#F<>)CH#CX>oraKPXlN#?sYb=p}!D*No0R%|&>t~qESDF<80 zeCMD{yK-~{Ll`!Ynqc`d2w6xY!^A?xPI5LcG1J*1xCD6v)c1_C*KmgxTogDpTVS|Y z9yA4iYQP7I!lpLKkhI;x4(w!_8npB4RrFz^g}34DOzu>5VvFs%)Zd~hWCoxXGqKFHyDhv!A~KU{ zceBi@(n+th@J_so1QE(vX(u)1Mn5!(j2k)gy6PW__qa>;Qb%n79?>BJu zges{^jgr~?15#>NP^{O-*nBPh`26z&xcxm##;%iOeNfJwkpBa$e8|FwB`fnJE8Zr$ zoNUgCCv8~_$qh+pR~gvHtp7imbf64ebZQtk;A1kz-Y9U^bUuF=9|;Il;qnvsq=Atl zbILJPx}^hs3ZIr{#EihC+FXGpk2f`^Gc%lJlw&6KgYai8d{*kWPA0FMSK4}n^y70D zJ}-H*Bya1IX=P9~xD~e<_(Bni7Y}#lU1!+B?HHx1GtzCEyj7E#*hZ!{ml4ykhpEZ~ z0&|qJ1p?{nLNb+bawhIjizNziXVq|~kfvJp<4(!s7o~m!`Y#1wIpQu0cjF$$d^?+U z()9MW;Lr*V>dp37WjNc9`z(A(rVZ20c%5O~j|U8VrC8uh+=qn+B_ty&x0XtWSCAwvsA4)W+ArRNsn>S|nK$>~aSLCSkuBt0Qm|8b6JMiu zbUFQnfd%=&Od*w6(C;i5baM3Hetg5ilVu&+JCqK?_!hon;M>KRsZ_Dfb{Sy5i>Ii; z;bfWxLvvSG3!PTr`xbs6_twPnQVOsKoB&>in?RjkAFwtVhFJsGCd(%iMps#fPYAah z!uY68ECTJ*)T~9Kis>`}phy@YtCbkD43m^0E5ig0-fP>K z%g{o-CP8ZpNsk(4q^;RbZv1#>rk(9H{J1(}l%5OmP6^IzdO#}aB&Kwj!}bne1ho+-nR(yEs2*>0x8F=PPmoe> zk9Y$UPRh0A$#TNcX=k>0eCi2kS6aYPo+C2>akQSJkTzLRSuK7zXo6Dq1C6poOx${y<5P>-|CNtvRa5m&~_o{UXL3g`K1 ztH94{nh9xoU#GXtEmk0YVhh}D^i9)s6t*L8H0Cq>Jujqau+%CaVwq|M&aK>cRFI@x znYekS5s1KH?`mjGQeALNxS~2)nmluCcbd_yGiB%VPTmm5PJ8)P!4$p2#uR&CbPVWQ znFtm#b?Cfd)1k@h3BGCa$j{aq^&aLo?`8Nd?*n+>!{0i7y`1elo?%Aoe$=<>2heys z=H8;F%;RsZ5;_l6crho>!;9Evc@uC9j^!6s#Xe71j87@7y7e|J_&i@UP65%##^VB$ zt4ouQ!}0#)l^m_f(XESb$AU5hi5xhIyJ}GqW_zQ22~xV;FOvxt6?bKx%)K{~jK>b* zgsuB=QqMs&^VzzNz!s08jj69{1a7oy3@;HZXjZImYwQ@q%QWo81qZQeYh%YjbaKX7 zjcXd$i2YdGUL94t8pYX-=ZvDaEm}Q>EgHsfK81y%q5E+{G;{#=UK}f*1J|KxaoZR! z*05zsTQoF=td1>m!EFN9jqh$Hj7tSPz9sGJj^I=|$wSv(y+kheMvYJ} zX*Z(A0lc-{Y#YU{sL7uT<#ah(iGEm;KZ+|x@$Lh7UwgH;Q1zE_jn*F0Pd7%;x46j| z!(J(EMZ38qYL-OTL~EqzuzXzK9*KsUjHP-s!Zmh9t4HyXcr+~EhbXvLbyh{q-pdWq zWO%FW8y~5aiyHdr<_P9T4bI^>bE&@cZtRf)RPW`cXfmUrL;J>`j)o56hOIQdj~>9s z)p!s7O>&)UJ}i9rRS7BZ4@y=QWvythSrV-&;SNW`5_d#CZf>{aim7zam0X<@X4@Ue%n~C5PAFidzn1|JKH_ z{Wut{AH|_j+&hXdw?=EC_51Nqv`#iYwilrhvBD#U-&5BfQoxMM&G9Dl`tgPlVNFJU z?e+Kv7i)Zi(iP7?JQ7;IUV)a6Vqbu5wayXHA_?%gQQE9ijCcecz&8~aa;yFrC9uI~ z;d=oV@Dh)OhKei%45esV$HamoO;WT+C$vdWHSi|Sp=-@rk z%P@$Q7{-~n46Auq@5EIcyBF*532eko=*2DQ!vnmOdJ<8Msxb|o{Fsro1sCBN{Dfiu zY;@tL_!*GBACF%!0qD3zKNi2_TNU{`j=cSZf8$#n^{^g)=Uct@45g_x$oZq5WHNuZCt33ENv0NV#XqEE>f$C(GPSWA|CExc zlNA0XB~vSF@oy=ax><&2`PP8vXuoS^hE_WGPe}*k)PdrgDKq3T$~VK~hj0EK`4;vt z$v43t(T-W;%*E9Pgn=Q0S6c=-<-*xuK`pnlpB238P>x1{=|=sm#M`m^$XBKC~4Zh1cO+P`2ti-lqwqiB3# zi>O_2sUuiIRv0{U`*p~*q^646SuX2#7sWt(6f%CN5$wv%ji9_m}>RbDpfPf@r;;3BwSzA(o!_eTB!U`R(8Dg z@KIjrql|ed>!zUeq4`Lsf}2P40v)oJraqI;IdqZ}X~ZYf9m>FBC19}P=Z*+UY2BtE6FC^-JtY-`4#=p z&;4r8X|b*6^au2Fe@{=J+1-R>Nt9Dg&Y7K=-S_sq&->2g?|=XN3%~{Z5JpH~-jq(l zE){f(lcyzXEn${&GLe%--OO3iE|*N3OG`Ssm6uMoWIKt}!sMd6n&2v>JCz~_JyWV! zSs6yVz{TcD?Mk^^vMApulJT~~=oGlzLQ*=8Vdm{b#+ziyGHa|F(z;`n){UIB!iWf* zY%UxyClHmUwDKFudx5>_C;GacDClNBF=IM1FRi42mb1Koy^kUUz|0$_6u6mgZe{0) zQGMq%!!(?6fwtk1C4u${Vje{sVj6nU7sWmdgt1>>&o6W3^{${)L*sule?mE-} z^o+cRB=VDZZzd(MZ@Aw3jAJp)$p}seTp-S4^~@Yo%p7aPd8~n%0=-n9GVF3e-j zGJ?|rU8#kkNk4o0w#?T!#7JXRFDi~vNTdzhp{rdPBX5$b7Gav)_QACl;MlFGnjB9? znToR-#xNoMxE2OM>U9}fgH6MGP_))_If4E4 z+U8fD$gGnbc^E-A`ODT)DL90_-_u{yK%J4CT-Cj|1C8|DC|4;u5fJ-7>G0S{bZgCE ze>Kg4cF;qt>%SYQkF44?R3-N*{x!6YK8iN(s1ism%$T;Ln^|%zMWf9#+{pD;b>XC{ z@;MC}dKi^5S!ZB!c=!58mZDg|Vi*~L>$~+Lqu!G-O+*W#^EEERRbOiZg&MK8>;NqJPsI3-KZOV<{Q zKa58LS6e{00~IV4OX|6aip8_Yi$Gw!HFtKPk_~AApK8$2r>f4XlykbngD4B0<1FAq zTgT5rg`KJ>C!b|y*)d9{hE*lhS7yU@GLD{ITeS2nWtS0)z+iHD%nYUEx{;M^WeZr- zP{5Q|A$*mya@BXsptcgq0)yp><0XovAxjlIqZi8sxunjz*ET2bSW7CR3RoI!)#W;w zY@Rd9t%`J-ai46CeD?4wUa~m$b2ZENg3kk^FR*8{ zKl(fN{)U6q$>1-p;F}y{Xy>SL?4^pm7~uSfyW@)EEevr*_;JiveU?x4t}S-_IUgY? zHJ$F(!8*~ln&`tp-yZRSpvF8t53MTfunG*TvgWgK8SLeBp7b4^c_y zXq<`L%2(9gy+o6jT@3@?SH1?72t^O&$V^j527``VZR*JF=8n`6$8%(yj$Ciyh~lSm z?&qGeL@5zeq7s;cxTIp&Ad&8mHd z4Qe{T-{<&(voBq(q1$0(+xu@&>?>|6>`(HkOm@(NuY)lPSG$TLgm2yPJ0G6!{{ulL B_OJi| literal 0 HcmV?d00001 diff --git a/net.solarnetwork.node.demandresponse.dretargetcost/build/eclipse/net/solarnetwork/node/demandresponse/dretargetcost/DRETargetCostDatumDataSource.properties b/net.solarnetwork.node.demandresponse.dretargetcost/build/eclipse/net/solarnetwork/node/demandresponse/dretargetcost/DRETargetCostDatumDataSource.properties new file mode 100644 index 000000000..2a14192d5 --- /dev/null +++ b/net.solarnetwork.node.demandresponse.dretargetcost/build/eclipse/net/solarnetwork/node/demandresponse/dretargetcost/DRETargetCostDatumDataSource.properties @@ -0,0 +1,26 @@ +title= Target Cost DR Engine +sourceId.key = Source ID +uid.key = UID +groupUID.key = Service Group +groupUID.desc = An optional group to include this service in. +triggerCronExpression.key = Schedule +triggerCronExpression.desc = A \ + \ + cron expression representing the schedule to sample data at. + +poweredDevices.propertyFilters['UID'].key = Generation Component +poweredDevices.propertyFilters['UID'].desc = The Service Name of the generation data source \ + to use for collecting current generation levels from. + +poweredDevices.propertyFilters['groupUID'].key = Generation Group +poweredDevices.propertyFilters['groupUID'].desc = The Service Group of a collection of \ + generation data sources to use for collecting current generation levels from. If this is configured, \ + this value and the configured Generation Component will be used to determine which \ + data sources to use. To use all sources within a group, leave Generation Component empty. + +energyCost.key = Energy Cost +energyCost.desc = The current price of energy + +drtargetCost.key = Target Energy Cost +drtargetCost.desc = The target price to power the devices. The system tries to get as close as possible \ +without going over \ No newline at end of file diff --git a/net.solarnetwork.node.demandresponse.dretargetcost/build/eclipse/net/solarnetwork/node/demandresponse/dretargetcost/DRSupportTools$NotChargeableDeviceException.class b/net.solarnetwork.node.demandresponse.dretargetcost/build/eclipse/net/solarnetwork/node/demandresponse/dretargetcost/DRSupportTools$NotChargeableDeviceException.class new file mode 100644 index 0000000000000000000000000000000000000000..869f36e68cc03c498ed5ecf302e545210af43f89 GIT binary patch literal 634 zcmcIh%Sr=55Ue(d(IlD}-@%LEK|Q$ORS_gnP=W^^If~<^(b3IJn3;`!hkxQp@Zbmd zQDW~Rh!-#79BR7yLD$s0f4sf{*v4`hV}wh?A@Vj+PJ##P?jvLS9QApiOy98|TH}Q} z4$9qdh^-G%=kzQc4y_C4)+T=K#D?~5R#n#tcX*^@K6s3I7_>EI6bY?6HBwQc%uRHf znxF^Ybwb0l(<$&?FEvG%JwLW>k|mt6B4Uz{oCYfkkauH5N@RpUWC~LQ}I;b4jz(*}N49Cz6F+ zE^At;Y&Mfu&?2zyn!2I}GODo@m|VD~r7eM;aAGQ$2u>vDqw!=aF&#=pC*uNLvC7t@ zW$MP#n84N$X)M*SX4FhUYlBCin{44kSXJ#ZWNchBk@RLe0Dw@nMy=L(V%p-4ci60nxUCXH)YuL zGMq?6g5lW`{Zj(UqG@XC;!P?LIvY%kM}ntgk<$7uo=;zux~U5pI^U6;oKA!y(Qs+Q zUOJJ_7EDGM7U&E|leUp)e7v;d09o~XSkK#bC>C{Pqz+eAKm!!VA)CssYNo&rXVd&t zgq-nIaJ=l-AsQ`i)yQoyG=JG>iFsJSL;ak60$Wb%hHjk_XdWDz5oigqO1x-Bz=shW zrh%AlXz{}Gf@Y?iXuD$Bw3?YwOX~WBD8>}LOd;)RE>Ze$60cAr)~>+8qIFkS zRM~PGg=Su@>K~^m6a)o!S6U^MQSmi z^Cf6hLao*|2ff{M6B99uDwxpSKVYFsjeJXb% zOJ}_hnD8NvNfxRkwnvA+F}Lc)0aG1L16S;7no!q@0rW_lgdy3$Y&`q1Bu)B|!ZfQ- z&)aLo=I*MAWNc_o!Fhp8Ph9B(@vK#0awcNA3cQ#>fU`q->uj2GdfJPNcuT>XT&q-3 zQ9K_mVV<)`FAiM0tD_23fqj+DPB)*)n#*dX>b$rt`C3=xpyrfz)mSMrEDX1H+~r8C z8%1gR`os@8DfZW;^IS5~%B6SQga#K74==`P#G7KY4{S zWH4HSlhy_^=bL z*g~WbHKC2z>%#5CTV1$=c$*7%5_h|B4{@&x?;zgk!hJ-$U3d@iJ{R6k{Im=A6F=j^ z2Z;w=cnBTzoi9)RKFbIKyb4}hhwtIYS9VCx@mB^2EaQ3p`kW2?czc1!gBS4<13$+5 zCLFho+~8dkv46O0&o?;Hgzs@`Jto%egY7i%8d@ODcGw!;;*;A{>5gr_*Ex#Q2yw?& zqA(&nd4|ZL_H%8V=q&%}SWKZk52ARTdmT5<*=~f0rQ1qZ`|y3l)_L1((>l?7;fh8n z(p5<06G`I1)Fy^I>KR^tOv7(%Fq|%W+)J~G&(Qt=Gqd+_;ZawAiN~$m1J>Mk@Z+J2 zuXjTr@<~apW(Yqcn%zKrm&7t-qqW2bC2?%z2Y5%m#@y%d$;-CIyVAGI^ljl$m%T-b z>-g5koB%)3x1Z_TFRbQYF^u0>F2B3-J6iO!7yU?Mu`0iEt>ke#TKxZ@Q^CbHg`R60 zJZ~u3P(8{1pj&^^t-t=SWLGv2SE{SC#N@87zg-NQRoBuchI{H57R`@o_$sEEQl}sE znts?p!`HrKnp_^UGAiA@naBL@XlS^vp5a}OY52ef!wr+Wzr^F}2Qdgm48bo(Tzrqw z4_^7gE=Oe8Ql*GxKXi-?mj>85Kun V_A%L-@Ck15D!*7}_;(wg{{WJtM`!>5 literal 0 HcmV?d00001 diff --git a/net.solarnetwork.node.demandresponse.dretargetcost/build/eclipse/net/solarnetwork/node/demandresponse/dretargetcost/MinimumDRStrategy.class b/net.solarnetwork.node.demandresponse.dretargetcost/build/eclipse/net/solarnetwork/node/demandresponse/dretargetcost/MinimumDRStrategy.class new file mode 100644 index 0000000000000000000000000000000000000000..1eec5ef1165d40406f9e70bda1585e19b39e9cfd GIT binary patch literal 2919 zcmcIm-E$LF6#w1yV?(m#BY{FeQJ_H6(nbNXHWh)!N-%A)Ev-~Ux5=eln(W5iP5JO+ z#&O0MAAIpacyyfcL0+6upfcn5?7!rU_`ACs*tRK%j!;=d)G_(qAy=p8Q*@|H;WGCjY%91A#U!;+WbX=|${pGr6RPB6rpyJ) z@am2fD6Ux^E;jeekw(e0o$RQT<$0sDRIpsnsh2#{w$27;(zPbOdInRI*=;bC8IK9IHsD1!d7q5r0=5}IP+{lqFeE@ zQ?Hd7vP}@!uA>d@NpxbDhMfYNR|iN(7j{#{^dy69I|7|OnMjlhp*)wwbJ(lldEVi% zg~qCm7w{qjV9D#u$M%MWNS1OM_6rRD+i=IxEs!QM@{@VFY?h?Th+&mv68msS$6@pc z=m8GgX}e~Y1a|ZU_wcWAmzmShD{$h;ez^5o&33%(8N)S8k=$!IvMPXi#yEjK zbi|PrXe&;PoGvDi!OJTDM+I&+@gNdgrJh^wV-v}f-HmuSZ;}|qkcQU;vd?%C9XSjKiJz2iz2coW z9HT0UN%Cb|17<$k$BbGofs;6;;SGV)%@2Aal~`zkyieP9#no{dX9SX_o1YvpY9W2Y zgg!bkGpEkX>v$8REH#W*dDieede^;fYb37)i}g%8zQbpA6qK^Alv(q9{RG~|n1&*6 z`MBPNS~|utp(ty4Ge)J(s>1S9P#W*(n8cKTwyfk6R}`5GA)9>`G|r9V0u4+p%5u>* z%5lt+yPmApl#z=%=I|buJv$)HT0)0ZQb2!S$7Os#-;HwlQECJZZ;1Ox6zR@*UB}~;z zSPGwFWTP6S+D=WefEe2nDJBa4w0U%Cm{n9{0gg&PtslW$b*y2XOsxw=Be!#a!RX0VsqWYcb;onO39ysTF6_r{hGGwna~$Ms6bD%4x^V#qaS4ZDvcum% zFTP~A{fgt)=*Mjg;5%ynh+}@7&iWy~6UJ#L_bpP7Ov6<^OAP!dZB8Y;2 E0dHnCqyPW_ literal 0 HcmV?d00001 diff --git a/net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/DRETargetCost.java b/net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/DRETargetCost.java new file mode 100644 index 000000000..c3eb4c00a --- /dev/null +++ b/net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/DRETargetCost.java @@ -0,0 +1,263 @@ +package net.solarnetwork.node.demandresponse.dretargetcost; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import net.solarnetwork.node.reactor.FeedbackInstructionHandler; +import net.solarnetwork.node.reactor.Instruction; +import net.solarnetwork.node.reactor.InstructionHandler; +import net.solarnetwork.node.reactor.support.BasicInstruction; + +/** + * Expirimental class looking into how a demand responce system my look like. + * Method names and API use will probably change in refactoring into a propper + * implementation. + * + * + * @author robert + * + */ +public class DRETargetCost { + private DRETargetCostDatumDataSource settings; + private Collection feedbackInstructionHandlers; + + // status variables for the datum source + private Integer numdrdevices = 0; + + public DRETargetCostDatumDataSource getSettings() { + return settings; + } + + // configured in OSGI + public void setSettings(DRETargetCostDatumDataSource settings) { + this.settings = settings; + + } + + protected void drupdate() { + + List drdevices = new ArrayList(); + + // the reason the mapping should be in String String is because perhapes + // in the future it could be JSON + Map> instructionMap = new HashMap>(); + for (FeedbackInstructionHandler handler : feedbackInstructionHandlers) { + + if (handler.handlesTopic(DRSupportTools.DRPARAMS_INSTRUCTION)) { + + BasicInstruction instr = new BasicInstruction(DRSupportTools.DRPARAMS_INSTRUCTION, new Date(), + Instruction.LOCAL_INSTRUCTION_ID, Instruction.LOCAL_INSTRUCTION_ID, null); + + // The devices want to know where the instruction came from for + // verification + instr.addParameter(settings.getUID(), ""); + + Map test = handler.processInstructionWithFeedback(instr).getResultParameters(); + if (test != null) { + + if (DRSupportTools.isDRCapable(test) && !DRSupportTools.isChargeable(test)) { + + drdevices.add(handler); + instructionMap.put(handler, test); + + } + } + + } + + } + + // This value is used by the datumdatasource + numdrdevices = drdevices.size(); + + // energyConsumption is energy used when not discharging + Integer energyConsumption = 0; + + // energyProduction is energy used when discharging + Integer energyProduction = 0; + for (FeedbackInstructionHandler d : drdevices) { + Map params = instructionMap.get(d); + + Integer wattValue = DRSupportTools.readWatts(params); + + energyConsumption += wattValue; + } + + Double newPrice = settings.getEnergyCost().doubleValue(); + + // need an object array cause I want to relate double with drdevice + // the reason im not using mapping is multiple drdevices could have the + // same double and I need to be able to sort the first column + Object[][] costArray = new Object[drdevices.size()][2]; + for (int i = 0; i < drdevices.size(); i++) { + FeedbackInstructionHandler d = drdevices.get(i); + Map params = instructionMap.get(d); + + Integer wattValue = DRSupportTools.readWatts(params); + Integer costValue = DRSupportTools.readEnergyDepreciationCost(params); + + // TODO double check you have done your maths correctly to factor in + // the convention of price being in KWh but using watts + costArray[i][0] = (costValue + newPrice) * wattValue; + costArray[i][1] = d; + } + + // simple sum to find the cost of running all these devices + Double totalCost = 0.0; + for (int i = 0; i < costArray.length; i++) { + totalCost += (Double) costArray[i][0]; + } + + // sorts the first columb which are doubles and keeps the relationship + // between cost and DRDevice + Arrays.sort(costArray, new Comparator() { + + @Override + public int compare(Object[] o1, Object[] o2) { + Double d1 = (Double) o1[0]; + Double d2 = (Double) o2[0]; + return d1.compareTo(d2); + } + + }); + // if this is true we need to reduce demand to get costs down + if (totalCost > settings.getDrtargetCost()) { + + // going from largest cost to smallest cost (this is just a design + // decision I made) + for (int i = drdevices.size() - 1; i >= 0; i--) { + FeedbackInstructionHandler d = (FeedbackInstructionHandler) costArray[i][1]; + Map params = instructionMap.get(d); + + Integer wattValue = DRSupportTools.readWatts(params); + Integer minValue = DRSupportTools.readMinWatts(params); + Integer energyCost = DRSupportTools.readEnergyDepreciationCost(params); + + // check if we can reduce consumption + if (wattValue > minValue) { + + // if we are here we need to reduce + Double reduceAmount = totalCost - settings.getDrtargetCost(); + Double energyReduction = reduceAmount / (energyCost + settings.getEnergyCost()); + Double appliedenergyReduction = (wattValue - energyReduction > minValue) ? energyReduction + : wattValue - minValue; + + System.out.println("reduceAmount" + appliedenergyReduction); + + // Im annoyed by this instruction because it is only reduce + // and not gain + sendShedInstruction(d, appliedenergyReduction); + + // we were able to increase to match demand no need for more + // devices to have DR + if (energyReduction.equals(appliedenergyReduction)) { + break; + } else { + // update the cost for the next devices to calcuate with + // TODO ensure that if we have changed batterys this is + // still effective + // One idea is to have a method to recalcuate the entire + // cost all over again + totalCost -= appliedenergyReduction * (energyCost + settings.getEnergyCost()); + } + + } + } + + // if true we need to increase demand + } else if (totalCost < settings.getDrtargetCost()) { + System.out.println("Debug should be here"); + // this time we start with the cheapest devices (my reasoning is + // that these devices most likely have room to power on more) so we + // can acheive nessisary requirements with little number of + // instructions + for (int i = 0; i < drdevices.size(); i++) { + FeedbackInstructionHandler d = (FeedbackInstructionHandler) costArray[i][1]; + Map params = instructionMap.get(d); + + Integer wattValue = DRSupportTools.readWatts(params); + Integer maxValue = DRSupportTools.readMaxWatts(params); + Integer energyCost = DRSupportTools.readEnergyDepreciationCost(params); + + if (wattValue < maxValue) { + + // if we are here it is okay to increase usage + Double increaseAmount = settings.getDrtargetCost() - totalCost; + + // energy increase is the amount of energy we want to + // increase by + Double energyIncrease = increaseAmount / (energyCost + settings.getEnergyCost()); + energyIncrease += wattValue; + + // appliedenergyIncrease is the value we are going to send + // as the maxValue can limit us to how much we can increase + // by + Double appliedenergyIncrease = Math.min(energyIncrease, maxValue); + Double energydelta = appliedenergyIncrease - wattValue; + + setWattageInstruction(d, appliedenergyIncrease); + + // we were able to increase to match demand no need for more + // devices to have DR + if (energyIncrease.equals(appliedenergyIncrease)) { + break; + } else { + // update the cost for the next devices to calcuate with + totalCost += energydelta * (energyCost + settings.getEnergyCost()); + } + + } + + } + } + + } + + // Instruction to set the wattage parameter on the device it uses the + // TOPIC_SET_CONTROL_PARAMETER insrtuction + private void setWattageInstruction(InstructionHandler handler, Double energyLevel) { + BasicInstruction instr = new BasicInstruction(InstructionHandler.TOPIC_SET_CONTROL_PARAMETER, new Date(), + Instruction.LOCAL_INSTRUCTION_ID, Instruction.LOCAL_INSTRUCTION_ID, null); + instr.addParameter("watts", energyLevel.toString()); + + // add as a param the source ID so devices can verify + instr.addParameter(settings.getUID(), ""); + + handler.processInstruction(instr); + } + + // Instruction to reduce wattage parameter on the device. The only reason Im + // using this instead of setWattageInstruction is because this instruction + // already exists + private void sendShedInstruction(InstructionHandler handler, Double shedamount) { + BasicInstruction instr = new BasicInstruction(InstructionHandler.TOPIC_SHED_LOAD, new Date(), + Instruction.LOCAL_INSTRUCTION_ID, Instruction.LOCAL_INSTRUCTION_ID, null); + + // the convention I saw used from other classes is that the value of the + // shedload is from the UID for verification + instr.addParameter(settings.getUID(), shedamount.toString()); + handler.processInstruction(instr); + } + + public Collection getFeedbackInstructionHandlers() { + return feedbackInstructionHandlers; + } + + // configured in OSGI we automatically get a collection of + // FeedbackInstructionHandlers as they come threw + public void setFeedbackInstructionHandlers(Collection feedbackInstructionHandlers) { + this.feedbackInstructionHandlers = feedbackInstructionHandlers; + } + + // This is for the DatumDataSource + public Integer getNumdrdevices() { + return numdrdevices; + } + +} diff --git a/net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/DRETargetCostDatumDataSource.java b/net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/DRETargetCostDatumDataSource.java new file mode 100644 index 000000000..156af39eb --- /dev/null +++ b/net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/DRETargetCostDatumDataSource.java @@ -0,0 +1,96 @@ +package net.solarnetwork.node.demandresponse.dretargetcost; + +import java.util.List; + +import net.solarnetwork.node.DatumDataSource; +import net.solarnetwork.node.domain.GeneralNodeEnergyStorageDatum; +import net.solarnetwork.node.settings.SettingSpecifier; +import net.solarnetwork.node.settings.SettingSpecifierProvider; +import net.solarnetwork.node.settings.support.BasicTextFieldSettingSpecifier; +import net.solarnetwork.node.support.DatumDataSourceSupport; + +public class DRETargetCostDatumDataSource extends DatumDataSourceSupport + implements SettingSpecifierProvider, DatumDataSource { + private Integer energyCost = 10; + + // we will use this value as a means to calebrate the DR a lower value means + // more likly to turn things off. The goal is to get the cost of powering + // devices as close to this value without going over. + private Integer drtargetCost = 10; + + private DRETargetCost drengine; + + @Override + public String getSettingUID() { + return "net.solarnetwork.node.demandresponse.dretargetcost"; + } + + @Override + public String getDisplayName() { + return "DR Engine"; + } + + @Override + public List getSettingSpecifiers() { + List results = getIdentifiableSettingSpecifiers(); + DRETargetCostDatumDataSource defaults = new DRETargetCostDatumDataSource(); + + // cost per watt hour for grid energy + results.add(new BasicTextFieldSettingSpecifier("energyCost", defaults.energyCost.toString())); + + // the target cost per hour. This strategy tries to preform a demand + // response to get closest without going over + results.add(new BasicTextFieldSettingSpecifier("drtargetCost", defaults.drtargetCost.toString())); + + return results; + } + + public Integer getEnergyCost() { + return energyCost; + } + + public void setEnergyCost(Integer energyCost) { + this.energyCost = energyCost; + + } + + public Integer getDrtargetCost() { + return drtargetCost; + } + + public void setDrtargetCost(Integer drtargetCost) { + this.drtargetCost = drtargetCost; + + } + + public DRETargetCost getDRInstance() { + return drengine; + } + + public void setDRInstance(DRETargetCost linkedInstance) { + this.drengine = linkedInstance; + linkedInstance.setSettings(this); + } + + @Override + public Class getDatumType() { + return GeneralNodeEnergyStorageDatum.class; + } + + @Override + public GeneralNodeEnergyStorageDatum readCurrentDatum() { + try { + drengine.drupdate(); + } catch (RuntimeException e) { + // the try catch is only for debugging I have noticed that + e.printStackTrace(); + } + + // the datum will contain num devices as well as watts cost? + GeneralNodeEnergyStorageDatum datum = new GeneralNodeEnergyStorageDatum(); + datum.putInstantaneousSampleValue("Num Devices", drengine.getNumdrdevices()); + datum.setSourceId(getUID()); + return datum; + } + +} diff --git a/net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/DRETargetCostDatumDataSource.properties b/net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/DRETargetCostDatumDataSource.properties new file mode 100644 index 000000000..2a14192d5 --- /dev/null +++ b/net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/DRETargetCostDatumDataSource.properties @@ -0,0 +1,26 @@ +title= Target Cost DR Engine +sourceId.key = Source ID +uid.key = UID +groupUID.key = Service Group +groupUID.desc = An optional group to include this service in. +triggerCronExpression.key = Schedule +triggerCronExpression.desc = A \ + \ + cron expression representing the schedule to sample data at. + +poweredDevices.propertyFilters['UID'].key = Generation Component +poweredDevices.propertyFilters['UID'].desc = The Service Name of the generation data source \ + to use for collecting current generation levels from. + +poweredDevices.propertyFilters['groupUID'].key = Generation Group +poweredDevices.propertyFilters['groupUID'].desc = The Service Group of a collection of \ + generation data sources to use for collecting current generation levels from. If this is configured, \ + this value and the configured Generation Component will be used to determine which \ + data sources to use. To use all sources within a group, leave Generation Component empty. + +energyCost.key = Energy Cost +energyCost.desc = The current price of energy + +drtargetCost.key = Target Energy Cost +drtargetCost.desc = The target price to power the devices. The system tries to get as close as possible \ +without going over \ No newline at end of file diff --git a/net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/DRSupportTools.java b/net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/DRSupportTools.java new file mode 100644 index 000000000..0305c6999 --- /dev/null +++ b/net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/DRSupportTools.java @@ -0,0 +1,320 @@ +package net.solarnetwork.node.demandresponse.dretargetcost; + +import java.util.Map; + +/** + * Helper class to make use of demand response. It is expected that one uses the + * constants and methods provided in this class to help you with demand response + * implementation. + * + * Please have a good read of the documentation in this class before using + * demand response. For examples of classes using this form of demand response + * see the following packages + * {@link net.solarnetwork.node.demandresponse.mockbattery} + * {@link net.solarnetwork.node.demandresponse.dretargetcost} + * + * There are two main concepts for demand response the DR device and the DR + * engine. The device is meant to be physical hardware while the engine is an + * algorithm providing instructions to the device. + * + * DR Instructions use the pre-existing Instructions interface. Demand response + * requires two way communication meaning DR Devices must implement the + * {@link net.solarnetwork.node.reactor.FeedbackInstructionHandler} interface + * and must handle the {@value #DRPARAMS_INSTRUCTION} instruction. + * + * There is no set standard for how a DR Engine gets a hold of DR Devices the + * strategy I have been using is to configure OSGI a reference list of + * FeedbackInstructionHandlers. + * + * A DR Device should only accept demand response instructions from one DREngine + * this is done by checking that the source id of the DR Engine is present in + * every demand response instruction parameters. The value of the parameter does + * not have to be anything (except for InstructionHandler.TOPIC_SHED_LOAD) it + * just must be non null. By convention I have been using empty string. + * + * The procedure for demand response is as follows. The DR Engine sends to its + * devices a DRPARAMS_INSTRUCTION those devices respond by sending back a map of + * parameters. The map is of datatype due to implementation of + * FeedbackInstructionHandler. However the parameters should all come as + * . The following parameters are expected in the responding map + * in order for a device to claim it supports DRPARAMS_INSTRUCTION. + * + * WATTS_PARAM = The current power in watts the device is operating at. This + * value should always be positive. + * + * MAXWATTS_PARAM = The maximum amount of power the device can run at. For + * chargeable devices this parameter is used for the maximum discharge rate. + * + * MINWATTS_PARAM = The minimum amount of power the device can run at. This + * parameter does not represent a physical constraint of the device. Instead + * just tells the DR Engine that this device is too important to be powered + * below this specific level. + * + * ENERGY_DEPRECIATION = The cost of running this device per watt hour. This + * cost can reflect a resource consumption or simply deprecation from use. + * + * DRREADY_PARAM = setting this parameter to "true" tells the DREngine that it + * can expect all of these above parameters in this map. Set it to "false" when + * the device is not ready to handle demand response at that time. + * + * CHARGEABLE_PARAM = Set this to "true" if your device support demand response + * of charging and discharging other set to "false" + * + * If CHARGEABLE_PARAM was set to true the next parameter is required otherwise + * this one can be ignored + * + * DISCHARGING_PARAM = Set to "true" if the device is discharging otherwise + * "false". Not being powered is seen as not discharging. + * + * MAXCHARGEWATTS_PARAM = Should only be present when device is chargeable the + * value of this parameter is the maximum number of watt hours the chargeable + * device can charge at. This is different to MAXWATTS_PARAM which is the + * maximum discharge rate. + * + * CHARGE_PERCENTAGE_PARAM = Should only be present when device is chargeable + * the value of this parameter should be a number between 0-100 representing the + * percentage of remaining capacity. + * + * MAXCHARGE_PARAM = Should only be present when device is chargeable. The + * number of watt hours of charge the device will have when fully charged. + * + * NOTE do not assume that any of these parameters are constant eg + * MAXWATTS_PARAM could change depending on environmental factors. You should + * query for these parameters every time you want to calculate a demand + * response. + * + * + * To send a demand response you have the choice of sending + * InstructionHandler.TOPIC_SHED_LOAD or + * InstructionHandler.TOPIC_SET_CONTROL_PARAMETER. + * + * When sending a TOPIC_SHED_LOAD instruction you map the DR Engine's sourceID + * to the shedamount as per the convention already in place by this instruction. + * The DR Device should reduce its consumption by the shedamount so long as it + * does not conflict with its minimum wattage. It is up to your implemention on + * how to handle this instruction when the parameters are invalid. You may + * choose to make assumptions and error correct or decline the instruction in + * your instruction status. + * + * When sending a TOPIC_SET_CONTROL_PARAMETER instruction remember to include + * the source ID of the DR Engine in the parameters. The parameters that are + * allowed to be changed with this instruction are WATTS_PARAM and if + * CHARGEABLE_PARAM was true then DISCHARGING_PARAM can be configured in this + * instruction. You are allow to configure multiple parameters in a single + * instruction. map the parameter you want changed to the value you want set. + * + * @author robert + * + */ +public class DRSupportTools { + + // The name of the instruction a DREngine sends to a DRDevice. The DRDevice + // should respond with a map of parameters + public static final String DRPARAMS_INSTRUCTION = "getDRDeviceInstance"; + + // Parameters that must be in response to a DRPARAMS_INSTRUCTION + public static final String WATTS_PARAM = "watts"; + public static final String MAXWATTS_PARAM = "maxwatts"; + public static final String MINWATTS_PARAM = "minwatts"; + public static final String ENERGY_DEPRECIATION = "energycost"; + public static final String DRREADY_PARAM = "drready"; + public static final String CHARGEABLE_PARAM = "chargeable"; + public static final String SOURCEID_PARAM = "sourceID"; + + // These parameters are required if CHARGEABLE_PARAM is true + public static final String DISCHARGING_PARAM = "isDischarging"; + public static final String MAXCHARGEWATTS_PARAM = "chargePower"; + public static final String CHARGE_PERCENTAGE_PARAM = "chargePercent"; + public static final String MAXCHARGE_PARAM = "maxcharge"; + + public static String readSourceID(Map params) { + try { + return (String) params.get(SOURCEID_PARAM); + } catch (ClassCastException e) { + return null; + } + + } + + /** + * Exception for when trying to access a parameter for chargeable device + * when CHARGEABLE_PARAM is false + * + * @author robert + * + */ + public static class NotChargeableDeviceException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + } + + /** + * Reads the wattage reading from the parameters. If there is no wattage + * parameter the method returns null. + * + * @param params + * The parameters of the device + * @return The wattage reading of the device + */ + public static Integer readWatts(Map params) { + return readValue(WATTS_PARAM, params); + } + + /** + * Reads the max watts reading from the parameters. If no wattage reading is + * present the method returns null. + * + * A max wattage reading is the maximum amount of watts the device can + * operate on. + * + * @param params + * The parameters of the device + * @return The wattage reading of the device + */ + public static Integer readMaxWatts(Map params) { + return readValue(MAXWATTS_PARAM, params); + } + + /** + * Reads the maximum watts a chargeable device can charge at. + * + * @param params + * The parameters of the device + * @return + */ + public static Integer readMaxChargingWatts(Map params) { + if (!isChargeable(params)) { + throw new NotChargeableDeviceException(); + } + return readValue(MAXCHARGEWATTS_PARAM, params); + } + + /** + * Reads a number from 0-100 representing the charge percentage of the + * chargeable device. Returns null if parameter is missing. + * + * throws NotChargeableDeviceException if method is called when device is + * not a chargeable device + * + * throws NumberFormatException if the read value is not between (inclusive) + * 0 and 100 + * + * @param params + * The parameters of the device + * @return + */ + public static Integer readChargePercent(Map params) { + if (!isChargeable(params)) { + throw new NotChargeableDeviceException(); + } + Integer value = readValue(CHARGE_PERCENTAGE_PARAM, params); + if (value != null && value < 0 || value > 100) { + throw new NumberFormatException(); + } + return readValue(CHARGE_PERCENTAGE_PARAM, params); + } + + /** + * Reads the number of watt hours this chargeable device has at maximum + * charge. + * + * @param params + * The parameters of the device + * @return + */ + public static Integer readMaxCharge(Map params) { + if (!isChargeable(params)) { + throw new NotChargeableDeviceException(); + } + return readValue(MAXCHARGE_PARAM, params); + } + + /** + * Reads the min watts reading from the paramters. If no wattage reading is + * present the method returns null. + * + * A min wattage reading is the minimum amount of the watts the device can + * operate on. + * + * @param params + * The parameters of the device + * @return + */ + public static Integer readMinWatts(Map params) { + return readValue(MINWATTS_PARAM, params); + } + + /** + * Reads the energy cost from the parameters. If no energy cost reading is + * present the method returns null. + * + * The energy cost is the price per watt to operate the device. + * + * @param params + * The parameters of the device + * @return + */ + public static Integer readEnergyDepreciationCost(Map params) { + return readValue(ENERGY_DEPRECIATION, params); + } + + /** + * Checks whether the parameters specify whether the device can handle + * demand size response. + * + * @param params + * The parameters of the device + * @return + */ + public static boolean isDRCapable(Map params) { + return readBoolean(DRREADY_PARAM, params); + } + + /** + * Checks the parameters to see if the type of device supports charging for + * example a battery + * + * @param params + * The parameters of the device + * @return + */ + public static boolean isChargeable(Map params) { + return readBoolean(CHARGEABLE_PARAM, params); + } + + /** + * Checks the parameters to see if the device is discharging. Should only be + * called after verifying the device is chargeable + * + * + * @param params + * The parameters of the device + * @return + */ + public static boolean isDischarging(Map params) { + if (!isChargeable(params)) { + throw new NotChargeableDeviceException(); + } + return readBoolean(DISCHARGING_PARAM, params); + } + + // TODO fix behaviour + private static boolean readBoolean(String paramname, Map params) { + // cast to string then check for boolean + // just realised this does not check for nulls or non strings + return Boolean.parseBoolean((String) params.get(paramname)); + } + + private static Integer readValue(String paramname, Map params) { + // set variable to null to avoid uninitalised warnings + Integer result = null; + try { + result = (int) Double.parseDouble((String) params.get(paramname)); + } finally { + // Do nothing, in the case there was an exception the returned value + // will be null + } + return result; + } +} diff --git a/net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/MinimumDRStrategy.java b/net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/MinimumDRStrategy.java new file mode 100644 index 000000000..8740dfc3d --- /dev/null +++ b/net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/MinimumDRStrategy.java @@ -0,0 +1,57 @@ +package net.solarnetwork.node.demandresponse.dretargetcost; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +import net.solarnetwork.node.reactor.FeedbackInstructionHandler; +import net.solarnetwork.node.reactor.Instruction; +import net.solarnetwork.node.reactor.InstructionHandler; +import net.solarnetwork.node.reactor.support.BasicInstruction; + +/** + * another DRStrategy this one just powers off as much as it can. Using this to + * test being able to change strategys + * + * @author robert + * + */ +public class MinimumDRStrategy { + private List handlers; + + public void drupdate() { + for (FeedbackInstructionHandler handler : handlers) { + if (handler.handlesTopic("getDRDeviceInstance")) { + + BasicInstruction instr = new BasicInstruction("getDRDeviceInstance", new Date(), + Instruction.LOCAL_INSTRUCTION_ID, Instruction.LOCAL_INSTRUCTION_ID, null); + + // The devices want to know where the instruction came from for + // verification + // TODO work on verification + // instr.addParameter(settings.getUID(), ""); + + Map params = handler.processInstructionWithFeedback(instr).getResultParameters(); + if (DRSupportTools.isDRCapable(params)) { + Integer watts = DRSupportTools.readWatts(params); + Integer minwatts = DRSupportTools.readMinWatts(params); + if (watts > minwatts) { + Integer shedamount = watts - minwatts; + // in this strategy we just lower power of all devices + // as much as we can + instr = new BasicInstruction(InstructionHandler.TOPIC_SHED_LOAD, new Date(), + Instruction.LOCAL_INSTRUCTION_ID, Instruction.LOCAL_INSTRUCTION_ID, null); + // TODO figure out verification + instr.addParameter("temp", shedamount.toString()); + handler.processInstruction(instr); + } + } + } + } + } + + public void setHandlers(List handlers) { + this.handlers = handlers; + + } +} diff --git a/net.solarnetwork.node.demandresponse.mockbattery/.classpath b/net.solarnetwork.node.demandresponse.mockbattery/.classpath new file mode 100644 index 000000000..2374a41aa --- /dev/null +++ b/net.solarnetwork.node.demandresponse.mockbattery/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/net.solarnetwork.node.demandresponse.mockbattery/.gitignore b/net.solarnetwork.node.demandresponse.mockbattery/.gitignore new file mode 100644 index 000000000..c3dca1b96 --- /dev/null +++ b/net.solarnetwork.node.demandresponse.mockbattery/.gitignore @@ -0,0 +1,2 @@ +/build +/target diff --git a/net.solarnetwork.node.demandresponse.mockbattery/.project b/net.solarnetwork.node.demandresponse.mockbattery/.project new file mode 100644 index 000000000..51d8740cc --- /dev/null +++ b/net.solarnetwork.node.demandresponse.mockbattery/.project @@ -0,0 +1,28 @@ + + + net.solarnetwork.node.demandresponse.mockbattery + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/net.solarnetwork.node.demandresponse.mockbattery/.settings/org.eclipse.jdt.core.prefs b/net.solarnetwork.node.demandresponse.mockbattery/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..c537b6306 --- /dev/null +++ b/net.solarnetwork.node.demandresponse.mockbattery/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/net.solarnetwork.node.demandresponse.mockbattery/.settings/org.eclipse.pde.core.prefs b/net.solarnetwork.node.demandresponse.mockbattery/.settings/org.eclipse.pde.core.prefs new file mode 100644 index 000000000..e8ff8be0b --- /dev/null +++ b/net.solarnetwork.node.demandresponse.mockbattery/.settings/org.eclipse.pde.core.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +pluginProject.equinox=false +pluginProject.extensions=false +resolve.requirebundle=false diff --git a/net.solarnetwork.node.demandresponse.mockbattery/META-INF/MANIFEST.MF b/net.solarnetwork.node.demandresponse.mockbattery/META-INF/MANIFEST.MF new file mode 100644 index 000000000..f42f1dce0 --- /dev/null +++ b/net.solarnetwork.node.demandresponse.mockbattery/META-INF/MANIFEST.MF @@ -0,0 +1,29 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: DRBattery +Bundle-SymbolicName: net.solarnetwork.node.demandresponse.mockbattery +Bundle-Version: 1.0.0 +Bundle-Vendor: SolarNetwork +Bundle-RequiredExecutionEnvironment: JavaSE-1.6 +Import-Package: build.eclipse.net.solarnetwork.node.demandresponse.dretargetcost, + net.solarnetwork.node;version="1.23.0", + net.solarnetwork.node.dao;version="1.8.0", + net.solarnetwork.node.demandresponse.dretargetcost, + net.solarnetwork.node.domain;version="1.11.0", + net.solarnetwork.node.job;version="1.13.4", + net.solarnetwork.node.reactor;version="1.3.0", + net.solarnetwork.node.reactor.support;version="1.3.0", + net.solarnetwork.node.settings;version="1.10.0", + net.solarnetwork.node.settings.support;version="1.8.0", + net.solarnetwork.node.support;version="1.14.0", + net.solarnetwork.node.util;version="1.7.2", + net.solarnetwork.util;version="1.28.0", + org.quartz;version="[2.2.3,3.0.0)", + org.quartz.simpl;version="[2.2.3,3.0.0)", + org.slf4j;version="[1.7.24,2.0.0)", + org.springframework.beans;version="[4.2.9.RELEASE,5.0.0)", + org.springframework.context;version="[4.2.9.RELEASE,5.0.0)", + org.springframework.context.support;version="[4.2.9.RELEASE,5.0.0)", + org.springframework.core;version="[4.2.9.RELEASE,5.0.0)", + org.springframework.scheduling.quartz;version="[4.2.9.RELEASE,5.0.0)" +Export-Package: net.solarnetwork.node.demandresponse.mockbattery diff --git a/net.solarnetwork.node.demandresponse.mockbattery/OSGI-INF/blueprint/module.xml b/net.solarnetwork.node.demandresponse.mockbattery/OSGI-INF/blueprint/module.xml new file mode 100644 index 000000000..b472e4a38 --- /dev/null +++ b/net.solarnetwork.node.demandresponse.mockbattery/OSGI-INF/blueprint/module.xml @@ -0,0 +1,137 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + net.solarnetwork.node.job.ManagedTriggerAndJobDetail + net.solarnetwork.node.job.ServiceProvider + net.solarnetwork.node.settings.SettingSpecifierProvider + net.solarnetwork.node.reactor.FeedbackInstructionHandler + + + + + + + + + + + + + + + + + + + + + + + + net.solarnetwork.node.DatumDataSource + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/net.solarnetwork.node.demandresponse.mockbattery/build.properties b/net.solarnetwork.node.demandresponse.mockbattery/build.properties new file mode 100644 index 000000000..668f1032f --- /dev/null +++ b/net.solarnetwork.node.demandresponse.mockbattery/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = build/eclipse/ +bin.includes = META-INF/,\ + . diff --git a/net.solarnetwork.node.demandresponse.mockbattery/src/net/solarnetwork/node/demandresponse/mockbattery/DRBattery.java b/net.solarnetwork.node.demandresponse.mockbattery/src/net/solarnetwork/node/demandresponse/mockbattery/DRBattery.java new file mode 100644 index 000000000..0d743754b --- /dev/null +++ b/net.solarnetwork.node.demandresponse.mockbattery/src/net/solarnetwork/node/demandresponse/mockbattery/DRBattery.java @@ -0,0 +1,204 @@ +package net.solarnetwork.node.demandresponse.mockbattery; + +import java.util.Date; +import java.util.Hashtable; +import java.util.Map; + +import net.solarnetwork.node.demandresponse.dretargetcost.DRSupportTools; +import net.solarnetwork.node.job.SimpleManagedTriggerAndJobDetail; +import net.solarnetwork.node.reactor.FeedbackInstructionHandler; +import net.solarnetwork.node.reactor.Instruction; +import net.solarnetwork.node.reactor.InstructionHandler; +import net.solarnetwork.node.reactor.InstructionStatus; +import net.solarnetwork.node.reactor.InstructionStatus.InstructionState; +import net.solarnetwork.node.reactor.support.BasicInstructionStatus; + +/** + * Class to handle demand response instructions for a battery. This class + * follows the demand response rules described in the DRSupportTools class. This + * implementation is an example of a chargeable device, this means it can change + * from charging and discharging states. To be able to send demand response + * instructions to this device you need to include the name of the DR Engine as + * a parameter to each instruction. An in-depth explanation on how instruction + * parameters are formatted see DRSupportTools. + * + * @author robert + * + */ +public class DRBattery extends SimpleManagedTriggerAndJobDetail implements FeedbackInstructionHandler { + + @Override + public boolean handlesTopic(String topic) { + if (topic.equals(InstructionHandler.TOPIC_SHED_LOAD)) { + return true; + } + if (topic.equals(InstructionHandler.TOPIC_SET_CONTROL_PARAMETER)) { + return true; + } + if (topic.equals(DRSupportTools.DRPARAMS_INSTRUCTION)) { + return true; + } + return false; + } + + @Override + public InstructionState processInstruction(Instruction instruction) { + // use the with feedback method and remove the feedback + InstructionStatus status = processInstructionWithFeedback(instruction); + return status.getAcknowledgedInstructionState(); + + } + + @Override + public InstructionStatus processInstructionWithFeedback(Instruction instruction) { + DRBatteryDatumDataSource settings = getDRBatterySettings(); + InstructionState state; + MockBattery battery = settings.getMockBattery(); + if (instruction.getTopic().equals(DRSupportTools.DRPARAMS_INSTRUCTION)) { + + Map map = new Hashtable(); + + // this mock is always ready for demand response + map.put(DRSupportTools.DRREADY_PARAM, "true"); + + map.put(DRSupportTools.WATTS_PARAM, new Double(Math.abs(settings.getMockBattery().readDraw())).toString()); + + // This device is chargeable, + map.put(DRSupportTools.CHARGEABLE_PARAM, "true"); + map.put(DRSupportTools.DISCHARGING_PARAM, new Boolean(settings.getMockBattery().readDraw() > 0).toString()); + + // A Battery only has a limited number of charge and discharge + // cycles. The price of using energy is related to the cost of the + // battery and the number of cycles it can do and the maximum + // capacity of a charge. + map.put(DRSupportTools.ENERGY_DEPRECIATION, + new Integer((int) (settings.getBatteryCost().doubleValue() + / (settings.getBatteryCycles().doubleValue() * settings.getBatteryMaxCharge() * 2.0))) + .toString()); + + // This device is allowed to completly power down + map.put(DRSupportTools.MINWATTS_PARAM, "0"); + + map.put(DRSupportTools.MAXWATTS_PARAM, settings.getMaxDraw().toString()); + map.put(DRSupportTools.SOURCEID_PARAM, settings.getUID()); + map.put(DRSupportTools.CHARGE_PERCENTAGE_PARAM, + new Integer((int) settings.getMockBattery().capacityFraction() * 100).toString()); + map.put(DRSupportTools.MAXCHARGE_PARAM, settings.getBatteryMaxCharge().toString()); + map.put(DRSupportTools.MAXCHARGEWATTS_PARAM, settings.getMaxChargeDraw().toString()); + + // send the feedback of the instruction + state = InstructionState.Completed; + InstructionStatus status = new BasicInstructionStatus(instruction.getId(), state, new Date(), null, map); + + return status; + + } + + // This instruction is a little strange in that you have to map the DR + // Engine name with the shed amount see DRSupportTools for better + // explanation. + if (instruction.getTopic().equals(InstructionHandler.TOPIC_SHED_LOAD)) { + // the shed load value should be here + String param = instruction.getParameterValue(settings.getDREngineName()); + + if (param != null) { + try { + double value = Double.parseDouble(param); + + double draw = battery.readDraw(); + + // negative draw for a mockbattery means we are charging + if (draw < 0) { + // to shed out load we need to add + draw = draw + value; + } else { + draw = draw - value; + } + + battery.setDraw(draw); + state = InstructionState.Completed; + + } catch (NumberFormatException e) { + // Incorrectly formatted parameters should be declined + state = InstructionState.Declined; + } + + } else { + state = InstructionState.Declined; + } + } else { + state = InstructionState.Declined; + } + + if (instruction.getTopic().equals(InstructionHandler.TOPIC_SET_CONTROL_PARAMETER)) { + // verify the instruction came from the accepted DREngine + if (instruction.getParameterValue(settings.getDREngineName()) != null) { + + // for a battery we need two parameters one is what the watt + // level needs to be set to the other is whether we are charging + // or discharging. + String param = instruction.getParameterValue(DRSupportTools.WATTS_PARAM); + String param2 = instruction.getParameterValue(DRSupportTools.DISCHARGING_PARAM); + + // check we got both parameters + if (param != null && param2 != null) { + try { + + double value = Double.parseDouble(param); + + // the discharge parameter should be in form "true" or + // "false" + boolean discharge = Boolean.parseBoolean(param2); + + if (value < 0) { + // negative value does not make sense decline rather + // than assume positive + state = InstructionState.Declined; + + } else if (value > settings.getMaxDraw()) { + // the implementation of mockbattery has a negative + // draw + // as charging and positive draw as discharging + if (discharge) { + battery.setDraw(settings.getMaxDraw()); + } else { + battery.setDraw(-settings.getMaxDraw()); + } + state = InstructionState.Completed; + + } else { + // the implementation of mockbattery has a negative + // draw + // as charging and positive draw as discharging + if (discharge) { + battery.setDraw(value); + } else { + battery.setDraw(-value); + } + state = InstructionState.Completed; + + } + + } catch (NumberFormatException e) { + state = InstructionState.Declined; + } + + } else { + state = InstructionState.Declined; + } + } + + } + + InstructionStatus status = new BasicInstructionStatus(instruction.getId(), state, new Date()); + + return status; + } + + public DRBatteryDatumDataSource getDRBatterySettings() { + // Because of the way the OSGI is configured this is the best way I know + // to get access to the settings + return (DRBatteryDatumDataSource) getSettingSpecifierProvider(); + } + +} diff --git a/net.solarnetwork.node.demandresponse.mockbattery/src/net/solarnetwork/node/demandresponse/mockbattery/DRBatteryDatumDataSource.java b/net.solarnetwork.node.demandresponse.mockbattery/src/net/solarnetwork/node/demandresponse/mockbattery/DRBatteryDatumDataSource.java new file mode 100644 index 000000000..019fc6d90 --- /dev/null +++ b/net.solarnetwork.node.demandresponse.mockbattery/src/net/solarnetwork/node/demandresponse/mockbattery/DRBatteryDatumDataSource.java @@ -0,0 +1,183 @@ +package net.solarnetwork.node.demandresponse.mockbattery; + +import java.util.Date; +import java.util.List; + +import net.solarnetwork.node.DatumDataSource; +import net.solarnetwork.node.domain.GeneralNodeEnergyStorageDatum; +import net.solarnetwork.node.settings.SettingSpecifier; +import net.solarnetwork.node.settings.SettingSpecifierProvider; +import net.solarnetwork.node.settings.support.BasicTextFieldSettingSpecifier; +import net.solarnetwork.node.settings.support.BasicTitleSettingSpecifier; +import net.solarnetwork.node.support.DatumDataSourceSupport; + +/** + * DatumDataSource for the Demand response battery. When datums are read + * calculates the current battery charge. + * + * @author robert + * + */ +public class DRBatteryDatumDataSource extends DatumDataSourceSupport + implements SettingSpecifierProvider, DatumDataSource { + + // Default values + private Double maxCharge = 10.0; + private Double charge = 10.0; + private Integer cost = 1000; + private Integer cycles = 10000; + private String drEngineName = "DREngine"; + private Double maxDraw = 1.0; + private Double maxChargeDraw = 1.0; + + // The settings the user configures will be applied to this mockbattery + private MockBattery mockbattery; + + @Override + public Class getDatumType() { + return GeneralNodeEnergyStorageDatum.class; + } + + @Override + public GeneralNodeEnergyStorageDatum readCurrentDatum() { + + // create new datum + GeneralNodeEnergyStorageDatum datum = new GeneralNodeEnergyStorageDatum(); + datum.setCreated(new Date()); + datum.setSourceId(getUID()); + + // populate the datum with values from the battery + MockBattery mb = getMockBattery(); + datum.setAvailableEnergy((long) mb.readCharge()); + datum.setAvailableEnergyPercentage(mb.capacityFraction()); + + // Status indication on the datum + if (mb.readDraw() == 0) { + datum.putStatusSampleValue("Mode", "Idle"); + } else if (mb.readDraw() > 0) { + datum.putStatusSampleValue("Mode", "Discharging"); + } else { + datum.putStatusSampleValue("Mode", "Charging"); + } + + return datum; + + } + + public String getDREngineName() { + return drEngineName; + } + + public void setDrEngineName(String drEngineName) { + this.drEngineName = drEngineName; + } + + @Override + public String getSettingUID() { + return "net.solarnetwork.node.demandresponse.mockbattery"; + } + + @Override + public String getDisplayName() { + return "Mock DR Battery"; + } + + @Override + public List getSettingSpecifiers() { + List results = super.getIdentifiableSettingSpecifiers(); + DRBatteryDatumDataSource defaults = new DRBatteryDatumDataSource(); + results.add(new BasicTextFieldSettingSpecifier("batteryMaxCharge", defaults.maxCharge.toString())); + results.add(new BasicTextFieldSettingSpecifier("maxDraw", defaults.maxDraw.toString())); + results.add(new BasicTextFieldSettingSpecifier("maxChargeDraw", defaults.maxChargeDraw.toString())); + results.add(new BasicTextFieldSettingSpecifier("batteryCharge", defaults.charge.toString())); + results.add(new BasicTextFieldSettingSpecifier("batteryCost", defaults.cost.toString())); + results.add(new BasicTextFieldSettingSpecifier("batteryCycles", defaults.cycles.toString())); + + // Display the calculated energycost on the screen + // Limitation is you need to refresh the settings page for it to update + results.add(new BasicTitleSettingSpecifier("energyCost", calcCost(), true)); + + // SourceID of the DREngine to accept demand response from + results.add(new BasicTextFieldSettingSpecifier("drEngineName", defaults.drEngineName)); + return results; + } + + // configured in OSGI + public void setMockBattery(MockBattery mockbattery) { + this.mockbattery = mockbattery; + } + + public MockBattery getMockBattery() { + return this.mockbattery; + } + + public void setBatteryMaxCharge(Double charge) { + if (charge != null) { + mockbattery.setMax(charge); + } + this.maxCharge = charge; + } + + public Double getBatteryMaxCharge() { + return maxCharge; + } + + public Double getMaxDraw() { + return maxDraw; + } + + public void setMaxDraw(Double maxDraw) { + this.maxDraw = maxDraw; + // the mock battery does not have a concept of max draw so this method + // needs to handle the logic + if (mockbattery.readDraw() > maxDraw) { + mockbattery.setDraw(maxDraw); + } + } + + public Double getMaxChargeDraw() { + return maxChargeDraw; + } + + public void setMaxChargeDraw(Double maxChargeDraw) { + this.maxChargeDraw = maxChargeDraw; + } + + public void setBatteryCharge(Double charge) { + if (charge != null) { + mockbattery.setCharge(charge); + } + this.charge = charge; + } + + // note this returns the charge value the user set in settings page not the + // current charge + // of the battery + public Double getBatteryCharge() { + return this.charge; + } + + public Integer getBatteryCost() { + return cost; + } + + public void setBatteryCost(Integer cost) { + this.cost = cost; + } + + public Integer getBatteryCycles() { + return cycles; + } + + public void setBatteryCycles(Integer cycles) { + this.cycles = cycles; + } + + // Used for showing the depreciation cost on the settings page, + private String calcCost() { + return new Double( + (getBatteryCost().doubleValue() / (getBatteryCycles().doubleValue() * getBatteryMaxCharge() * 2.0))) + .toString(); + } + +} diff --git a/net.solarnetwork.node.demandresponse.mockbattery/src/net/solarnetwork/node/demandresponse/mockbattery/DRBatteryDatumDataSource.properties b/net.solarnetwork.node.demandresponse.mockbattery/src/net/solarnetwork/node/demandresponse/mockbattery/DRBatteryDatumDataSource.properties new file mode 100644 index 000000000..c3fb12157 --- /dev/null +++ b/net.solarnetwork.node.demandresponse.mockbattery/src/net/solarnetwork/node/demandresponse/mockbattery/DRBatteryDatumDataSource.properties @@ -0,0 +1,33 @@ +title= Mock DR Battery +uid.key = Source ID +groupUID.key = Service Group +groupUID.desc = An optional group to include this service in. +triggerCronExpression.key = Schedule +triggerCronExpression.desc = A \ + \ + cron expression representing the schedule to sample data at. + +batteryCharge.key = Battery Starting Charge +batteryCharge.desc = Setting this value will force the charge of the battery for that instant. The battery \ + is then free to drain or charge from that point. Units W/h + +batteryMaxCharge.key = Max Battery Charge +batteryMaxCharge.desc = The maximum charge the battery can have. Units W/h + +maxChargeDraw.key = Max Charging Rate +maxChargeDraw.desc = Maximum battery charging power in W/h + +batteryCost.key = Battery Cost +batteryCost.desc = The price of the mock battery + +batteryCycles.key = Battery Cycles +batteryCycles.desc = The number of charge discharge cycles the battery supports + +maxDraw.key = Max Discharge Rate +maxDraw.desc = Maximum battery discharge power + +energyCost.key = Hardware Depreciation +energyCost.desc = The depreciation cost per watt hour of using the battery value is based on the cost of battery and number of cycles + +drEngineName.key = DR Engine +drEngineName.desc = The name of the source to accept demand response queries \ No newline at end of file diff --git a/net.solarnetwork.node.demandresponse.mockbattery/src/net/solarnetwork/node/demandresponse/mockbattery/MockBattery.java b/net.solarnetwork.node.demandresponse.mockbattery/src/net/solarnetwork/node/demandresponse/mockbattery/MockBattery.java new file mode 100644 index 000000000..7431a8a0d --- /dev/null +++ b/net.solarnetwork.node.demandresponse.mockbattery/src/net/solarnetwork/node/demandresponse/mockbattery/MockBattery.java @@ -0,0 +1,135 @@ +package net.solarnetwork.node.demandresponse.mockbattery; + +public class MockBattery { + private Double maxcapacity = null; + private double charge; + private double draw; + private long lastsample; + + public MockBattery(double maxcapacity) { + if (maxcapacity < 0) { + throw new IllegalArgumentException(); + } + this.maxcapacity = maxcapacity; + setCharge(0); + setDraw(0); + } + + public MockBattery() { + this(10.0); + } + + /** + * Sets the maximal charge is kWh of the battery. If the battery's charge is + * greater than the new maxcharge there will be no error. However be sure to + * call the readCharge method to update the charge value to be within + * bounds. + * + * @param maxcapacity + */ + public void setMax(double maxcapacity) { + // you cannot have no or negative capacity keep current value if + // argument is invalid + if (maxcapacity > 0) { + this.maxcapacity = maxcapacity; + } + } + + /** + * Forces the battery to a specific charge at that instant. Try to set a + * negative charge will put the battery at 0. Trying to set the charge + * beyond max charge will have the battery set to max charge + * + * @param charge + */ + public void setCharge(double charge) { + this.lastsample = readTime(); + // can't have negative charge,if that happens we keep the current value + if (charge >= 0.0) { + this.charge = Math.min(charge, this.maxcapacity); + + } + + } + + // returns the time difference in hours between now and lastsample reading. + // This is used for calculating the battery's charge. + private double deltaTimeHours() { + long oldtime = this.lastsample; + long currenttime = readTime(); + this.lastsample = currenttime; + double delta = currenttime - oldtime; + delta = delta / 1000 / 60 / 60; + return delta; + } + + /** + * Returns how many kWh of charge the battery has + * + * @return Charge (kWh) + */ + public double readCharge() { + if (this.maxcapacity == null) { + throw new RuntimeException(); + } + double delta = deltaTimeHours(); + double newcharge = this.charge - this.draw * delta; + if (newcharge < 0.0) { + newcharge = 0; + } + if (newcharge > this.maxcapacity) { + newcharge = this.maxcapacity; + } + setCharge(newcharge); + return newcharge; + } + + /** + * + * @return the powerDraw of the battery (kWh) + */ + public double readDraw() { + if (this.maxcapacity == null) { + throw new RuntimeException(); + } + if (this.draw < 0 && this.charge == this.maxcapacity) { + // can't charge what is already fully charged + return 0; + } else if (this.draw > 0 && this.charge == 0) { + // can't draw from empty battery + return 0; + } else { + return this.draw; + } + } + + /** + * returns the fraction of remaining battery capacity as a value between 0 + * and 1. Multiply this value by 100 to get remaining capacity as a + * percentage. + * + * @return remaining battery life + */ + public float capacityFraction() { + if (this.maxcapacity == null) { + throw new RuntimeException(); + } + return (float) (readCharge() / this.maxcapacity); + } + + /** + * Sets the draw of the battery. The mock battery does not have a max or min + * draw. A negative draw value means you are charging battery and positive + * value means you are draining the battery + * + * @param draw + */ + public void setDraw(double draw) { + readCharge(); + this.draw = draw; + } + + public long readTime() { + return System.currentTimeMillis(); + } +} diff --git a/net.solarnetwork.node.demandresponse.mockdrconsumer/.classpath b/net.solarnetwork.node.demandresponse.mockdrconsumer/.classpath new file mode 100644 index 000000000..2374a41aa --- /dev/null +++ b/net.solarnetwork.node.demandresponse.mockdrconsumer/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/net.solarnetwork.node.demandresponse.mockdrconsumer/.gitignore b/net.solarnetwork.node.demandresponse.mockdrconsumer/.gitignore new file mode 100644 index 000000000..c3dca1b96 --- /dev/null +++ b/net.solarnetwork.node.demandresponse.mockdrconsumer/.gitignore @@ -0,0 +1,2 @@ +/build +/target diff --git a/net.solarnetwork.node.demandresponse.mockdrconsumer/.project b/net.solarnetwork.node.demandresponse.mockdrconsumer/.project new file mode 100644 index 000000000..00eee3778 --- /dev/null +++ b/net.solarnetwork.node.demandresponse.mockdrconsumer/.project @@ -0,0 +1,28 @@ + + + net.solarnetwork.node.demandresponse.mockdrconsumer + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/net.solarnetwork.node.demandresponse.mockdrconsumer/.settings/org.eclipse.jdt.core.prefs b/net.solarnetwork.node.demandresponse.mockdrconsumer/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..c537b6306 --- /dev/null +++ b/net.solarnetwork.node.demandresponse.mockdrconsumer/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/net.solarnetwork.node.demandresponse.mockdrconsumer/.settings/org.eclipse.pde.core.prefs b/net.solarnetwork.node.demandresponse.mockdrconsumer/.settings/org.eclipse.pde.core.prefs new file mode 100644 index 000000000..f29e940a0 --- /dev/null +++ b/net.solarnetwork.node.demandresponse.mockdrconsumer/.settings/org.eclipse.pde.core.prefs @@ -0,0 +1,3 @@ +eclipse.preferences.version=1 +pluginProject.extensions=false +resolve.requirebundle=false diff --git a/net.solarnetwork.node.demandresponse.mockdrconsumer/META-INF/MANIFEST.MF b/net.solarnetwork.node.demandresponse.mockdrconsumer/META-INF/MANIFEST.MF new file mode 100644 index 000000000..dbc151872 --- /dev/null +++ b/net.solarnetwork.node.demandresponse.mockdrconsumer/META-INF/MANIFEST.MF @@ -0,0 +1,29 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Mock DRDevice +Bundle-SymbolicName: net.solarnetwork.node.demandresponse.mockdrconsumer +Bundle-Version: 1.0.0 +Bundle-Vendor: SolarNetwork +Bundle-RequiredExecutionEnvironment: JavaSE-1.6 +Import-Package: + build.eclipse.net.solarnetwork.node.demandresponse.dretargetcost, + net.solarnetwork.node;version="1.22.0", + net.solarnetwork.node.dao;version="1.8.0", + net.solarnetwork.node.demandresponse.dretargetcost, + net.solarnetwork.node.domain;version="1.11.0", + net.solarnetwork.node.job;version="1.13.4", + net.solarnetwork.node.reactor;version="1.3.0", + net.solarnetwork.node.reactor.support;version="1.3.0", + net.solarnetwork.node.settings;version="1.10.0", + net.solarnetwork.node.settings.support;version="1.8.0", + net.solarnetwork.node.support;version="1.14.0", + net.solarnetwork.node.util;version="1.7.2", + net.solarnetwork.util;version="1.28.0", + org.quartz;version="[2.2.3,3.0)", + org.quartz.simpl;version="[2.2.3,3.0)", + org.slf4j;version="[1.7.24,2.0)", + org.springframework.beans;version="[4.2.9,5.0)", + org.springframework.context;version="[4.2.9,5.0)", + org.springframework.context.support;version="[4.2.9,5.0)", + org.springframework.core;version="[4.2.9,5.0)", + org.springframework.scheduling.quartz;version="[4.2.9,5.0)" diff --git a/net.solarnetwork.node.demandresponse.mockdrconsumer/OSGI-INF/blueprint/module.xml b/net.solarnetwork.node.demandresponse.mockdrconsumer/OSGI-INF/blueprint/module.xml new file mode 100644 index 000000000..8685d263f --- /dev/null +++ b/net.solarnetwork.node.demandresponse.mockdrconsumer/OSGI-INF/blueprint/module.xml @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + net.solarnetwork.node.job.ManagedTriggerAndJobDetail + net.solarnetwork.node.job.ServiceProvider + net.solarnetwork.node.settings.SettingSpecifierProvider + net.solarnetwork.node.reactor.FeedbackInstructionHandler + + + + + + + + + + + + + + + + + + + + + + + + net.solarnetwork.node.DatumDataSource + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/net.solarnetwork.node.demandresponse.mockdrconsumer/bin/build/eclipse/net/solarnetwork/node/datum/energymeter/mock/MockEnergyMeterDatumSource.properties b/net.solarnetwork.node.demandresponse.mockdrconsumer/bin/build/eclipse/net/solarnetwork/node/datum/energymeter/mock/MockEnergyMeterDatumSource.properties new file mode 100644 index 000000000..d3bd6a7ab --- /dev/null +++ b/net.solarnetwork.node.demandresponse.mockdrconsumer/bin/build/eclipse/net/solarnetwork/node/datum/energymeter/mock/MockEnergyMeterDatumSource.properties @@ -0,0 +1,32 @@ +title = Mock Energy Meter + +triggerCronExpression.key = Schedule +triggerCronExpression.desc = The cron expression controlling the sampling rate of the mock data. + +uid.key = Service Name +uid.desc = A unique name to identify this service with. +groupUID.key = Service Group +groupUID.desc = An optional group to include this service in. +sourceId.key = Source ID +sourceId.desc = A unique source identifier. + +voltage.key = RMS Voltage +voltage.desc = RMS of the mock power supply in volts. +frequency.key = Frequency +frequency.desc = Frequency of mock AC power supply, in Hz. +resistance.key = Resistance +resistance.desc = Resistance of the mock circuit, in Ohms. +inductance.key = Inductance +inductance.desc = Inductance of the mock circuit, in mirco Henry. +randomness.key = Toggle Randomness +randomness.desc = Set to true to have voltage and frequency deviate there values. +voltdev.key = Voltage Deviation +voltdev.desc = When randomness is on the source voltage will have random deviations +- this value. +freqdev.key = Frequency Deviation +freqdev.desc = When randomness is on the source frequency will have random deviation +- this value. + +resistanceDeviation.key = Resistance Deviation +resistanceDeviation.desc = When randomness is on the resistance will have random deviations +- this value. + +inductanceDeviation.key = Inductance Deviation +inductanceDeviation.desc = When randomness is on the inductance will have random deviations +- this value. diff --git a/net.solarnetwork.node.demandresponse.mockdrconsumer/build.properties b/net.solarnetwork.node.demandresponse.mockdrconsumer/build.properties new file mode 100644 index 000000000..4fe8ae9bc --- /dev/null +++ b/net.solarnetwork.node.demandresponse.mockdrconsumer/build.properties @@ -0,0 +1,5 @@ +source.. = src/ +output.. = build/eclipse/ +bin.includes = META-INF/,\ + .,\ + OSGI-INF/ \ No newline at end of file diff --git a/net.solarnetwork.node.demandresponse.mockdrconsumer/src/net/solarnetwork/node/demandresponse/mockdrconsumer/MockDRConsumer.java b/net.solarnetwork.node.demandresponse.mockdrconsumer/src/net/solarnetwork/node/demandresponse/mockdrconsumer/MockDRConsumer.java new file mode 100644 index 000000000..7ba2b4b09 --- /dev/null +++ b/net.solarnetwork.node.demandresponse.mockdrconsumer/src/net/solarnetwork/node/demandresponse/mockdrconsumer/MockDRConsumer.java @@ -0,0 +1,192 @@ +/* ================================================================== + * MockMeterDataSource.java - 10/06/2015 1:28:07 pm + * + * Copyright 2017 SolarNetwork.net Dev Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + * ================================================================== + */ + +package net.solarnetwork.node.demandresponse.mockdrconsumer; + +import java.util.Date; +import java.util.Hashtable; +import java.util.Map; + +import net.solarnetwork.node.DatumDataSource; +import net.solarnetwork.node.demandresponse.dretargetcost.DRSupportTools; +import net.solarnetwork.node.job.SimpleManagedTriggerAndJobDetail; +import net.solarnetwork.node.reactor.FeedbackInstructionHandler; +import net.solarnetwork.node.reactor.Instruction; +import net.solarnetwork.node.reactor.InstructionHandler; +import net.solarnetwork.node.reactor.InstructionStatus; +import net.solarnetwork.node.reactor.InstructionStatus.InstructionState; +import net.solarnetwork.node.reactor.support.BasicInstructionStatus; +import net.solarnetwork.node.settings.SettingSpecifierProvider; + +/** + * Mock plugin to be the source of values for a GeneralNodeACEnergyDatum, this + * mock tries to simulate a AC circuit containing a resister and inductor in + * series. + * + *

+ * This class implements {@link SettingSpecifierProvider} and + * {@link DatumDataSource} + *

+ * + * @author robert + * @version 1.0 + */ +public class MockDRConsumer extends SimpleManagedTriggerAndJobDetail implements FeedbackInstructionHandler { + + @Override + public boolean handlesTopic(String topic) { + if (topic.equals(InstructionHandler.TOPIC_SHED_LOAD)) { + return true; + } else if (topic.equals(InstructionHandler.TOPIC_SET_CONTROL_PARAMETER)) { + return true; + } else if (topic.equals(DRSupportTools.DRPARAMS_INSTRUCTION)) { + return true; + } + return false; + } + + @Override + // process an instruction just returning the status and not any feedback + public InstructionState processInstruction(Instruction instruction) { + InstructionStatus status = processInstructionWithFeedback(instruction); + return status.getAcknowledgedInstructionState(); + } + + @Override + /** + * List of supported Instructions getDRDeviceInstance, Shed load, set + * control parameter + */ + public InstructionStatus processInstructionWithFeedback(Instruction instruction) { + InstructionState state; + MockDRConsumerDatumDataSource settings = getSettings(); + + if (instruction.getTopic().equals(DRSupportTools.DRPARAMS_INSTRUCTION)) { + Map map = new Hashtable(); + + // check that this instruction came from the accepted source + if (instruction.getParameterValue(settings.getDrsource()) == null) { + // if not decline the instruction + state = InstructionState.Declined; + } else { + + state = InstructionState.Completed; + + // put values in the parameter map + map.put(DRSupportTools.DRREADY_PARAM, "true"); + map.put(DRSupportTools.WATTS_PARAM, settings.getWatts().toString()); + map.put(DRSupportTools.ENERGY_DEPRECIATION, settings.getEnergycost().toString()); + map.put(DRSupportTools.MINWATTS_PARAM, settings.getMinwatts().toString()); + map.put(DRSupportTools.MAXWATTS_PARAM, settings.getMaxwatts().toString()); + map.put(DRSupportTools.SOURCEID_PARAM, settings.getUID()); + } + + InstructionStatus status = new BasicInstructionStatus(instruction.getId(), state, new Date(), null, map); + return status; + + } + + // The shed load instruction reduces the wattage value by the set amount + if (instruction.getTopic().equals(InstructionHandler.TOPIC_SHED_LOAD)) { + // the value to shed should be mapped to the name of the drsource as + // was the convention used + // by this instruction in classes not written by me. + String param = instruction.getParameterValue(settings.getDrsource()); + if (param != null) { + try { + + // I did not see anywhere it previous uses of this + // instruction the requirment it had to be an interger + // while DRDevice is set to integer values I read a double + // and turn it to an int + double value = Double.parseDouble(param) + 0.5;// 0.5 for + // rounding + + // TODO shed load should only be able to reduce to minwatts + // BUG + value = settings.getWatts() - value; + if (value < settings.getMinwatts()) { + settings.setWatts(settings.getMinwatts()); + } else if (value > settings.getMaxwatts()) { + settings.setWatts(settings.getMaxwatts()); + } else { + settings.setWatts((int) value); + } + + state = InstructionState.Completed; + } catch (NumberFormatException e) { + + // if we cannot parse any number decline the instruction + // because something went wrong + state = InstructionState.Declined; + } + + } else { + + // instruction came from an untrusted source decline the + // instruction + state = InstructionState.Declined; + } + + // this instruction sets the wattage to a specific value rather than + // subtracting it like shed load + // it is mainly used for increasing the wattage reading however it + // can be used to reduce + } else if (instruction.getTopic().equals(InstructionHandler.TOPIC_SET_CONTROL_PARAMETER)) { + + String param = instruction.getParameterValue("watts"); + // be sure the instruction came from the accepted DR Engine + if (instruction.getParameterValue(settings.getDrsource()) != null && param != null) { + try { + double value = Double.parseDouble(param); + if (value < 0) { + settings.setWatts(0); + + } else if (value > settings.getMaxwatts()) { + settings.setWatts(settings.getMaxwatts()); + + } else { + settings.setWatts((int) value); + } + state = InstructionState.Completed; + } catch (NumberFormatException e) { + state = InstructionState.Declined; + } + + } else { + state = InstructionState.Declined; + } + } else { + state = InstructionState.Declined; + } + + InstructionStatus status = new BasicInstructionStatus(instruction.getId(), state, new Date()); + + return status; + } + + public MockDRConsumerDatumDataSource getSettings() { + + return (MockDRConsumerDatumDataSource) getSettingSpecifierProvider(); + } + +} diff --git a/net.solarnetwork.node.demandresponse.mockdrconsumer/src/net/solarnetwork/node/demandresponse/mockdrconsumer/MockDRConsumerDatumDataSource.java b/net.solarnetwork.node.demandresponse.mockdrconsumer/src/net/solarnetwork/node/demandresponse/mockdrconsumer/MockDRConsumerDatumDataSource.java new file mode 100644 index 000000000..9409fc0a3 --- /dev/null +++ b/net.solarnetwork.node.demandresponse.mockdrconsumer/src/net/solarnetwork/node/demandresponse/mockdrconsumer/MockDRConsumerDatumDataSource.java @@ -0,0 +1,109 @@ +package net.solarnetwork.node.demandresponse.mockdrconsumer; + +import java.util.Date; +import java.util.List; + +import net.solarnetwork.node.DatumDataSource; +import net.solarnetwork.node.domain.GeneralNodeACEnergyDatum; +import net.solarnetwork.node.settings.SettingSpecifier; +import net.solarnetwork.node.settings.SettingSpecifierProvider; +import net.solarnetwork.node.settings.support.BasicTextFieldSettingSpecifier; +import net.solarnetwork.node.support.DatumDataSourceSupport; + +public class MockDRConsumerDatumDataSource extends DatumDataSourceSupport + implements SettingSpecifierProvider, DatumDataSource { + + private Integer minwatts = 0; + private Integer maxwatts = 10; + private Integer energycost = 1; + private Integer watts = 0; + private String drsource = ""; + + @Override + public String getSettingUID() { + return "net.solarnetwork.node.demandresponse.mockdrconsumer"; + } + + @Override + public String getDisplayName() { + return "Mock DR Device"; + } + + @Override + public List getSettingSpecifiers() { + MockDRConsumerDatumDataSource defaults = new MockDRConsumerDatumDataSource(); + List results = super.getIdentifiableSettingSpecifiers(); + + // user enters text + results.add(new BasicTextFieldSettingSpecifier("minwatts", defaults.minwatts.toString())); + results.add(new BasicTextFieldSettingSpecifier("maxwatts", defaults.maxwatts.toString())); + results.add(new BasicTextFieldSettingSpecifier("energycost", defaults.energycost.toString())); + results.add(new BasicTextFieldSettingSpecifier("drsource", defaults.drsource)); + return results; + } + + public Integer getMinwatts() { + return minwatts; + } + + public void setMinwatts(Integer minwatts) { + if (watts < minwatts) { + watts = minwatts; + } + this.minwatts = minwatts; + } + + public Integer getMaxwatts() { + return maxwatts; + } + + public void setMaxwatts(Integer maxwatts) { + if (watts > maxwatts) { + watts = maxwatts; + } + this.maxwatts = maxwatts; + } + + public Integer getEnergycost() { + return energycost; + } + + public void setEnergycost(Integer energycost) { + this.energycost = energycost; + } + + // This is the sourceID of the DRAnouncer to which the device will follow + public String getDrsource() { + return drsource; + } + + public void setDrsource(String drsource) { + this.drsource = drsource; + } + + // protected as this is not set via the settings page but instead via demand + // response + protected void setWatts(Integer watts) { + this.watts = watts; + } + + public Integer getWatts() { + return watts; + } + + @Override + public Class getDatumType() { + // TODO Auto-generated method stub + return GeneralNodeACEnergyDatum.class; + } + + @Override + public GeneralNodeACEnergyDatum readCurrentDatum() { + GeneralNodeACEnergyDatum datum = new GeneralNodeACEnergyDatum(); + datum.setCreated(new Date()); + datum.setSourceId(getUID()); + datum.setWatts(getWatts()); + return datum; + } + +} diff --git a/net.solarnetwork.node.demandresponse.mockdrconsumer/src/net/solarnetwork/node/demandresponse/mockdrconsumer/MockDRConsumerDatumDataSource.properties b/net.solarnetwork.node.demandresponse.mockdrconsumer/src/net/solarnetwork/node/demandresponse/mockdrconsumer/MockDRConsumerDatumDataSource.properties new file mode 100644 index 000000000..b3a739785 --- /dev/null +++ b/net.solarnetwork.node.demandresponse.mockdrconsumer/src/net/solarnetwork/node/demandresponse/mockdrconsumer/MockDRConsumerDatumDataSource.properties @@ -0,0 +1,15 @@ +title = Mock DR Consumer +triggerCronExpression.key = Schedule +triggerCronExpression.desc = The cron expression controlling the sampling rate of the mock data. +uid.key = Service Name +uid.desc = A unique name to identify this service with. +groupUID.key = Service Group +groupUID.desc = An optional group to include this service in. +minwatts.key = Min Watts +minwatts.desc = Minimum Watts +maxwatts.key = Max Watts +maxwatts.desc = Maximum Watts +energycost.key = Depreciation Cost +energycost.desc = This value represents how much it costs to operate this device per watt on energy. This is value can be used by demand response strageys to plan a demand response. +drsource.key = DR SourceID +drsource.desc = The sourceID of a DR Engine for which this DRDevice shall accept instructions from \ No newline at end of file diff --git a/net.solarnetwork.node.demandresponse.mockdrconsumer/src/net/solarnetwork/node/demandresponse/mockdrconsumer/MockDRDeviceSettings.java b/net.solarnetwork.node.demandresponse.mockdrconsumer/src/net/solarnetwork/node/demandresponse/mockdrconsumer/MockDRDeviceSettings.java new file mode 100644 index 000000000..2e01cb184 --- /dev/null +++ b/net.solarnetwork.node.demandresponse.mockdrconsumer/src/net/solarnetwork/node/demandresponse/mockdrconsumer/MockDRDeviceSettings.java @@ -0,0 +1,92 @@ +package net.solarnetwork.node.demandresponse.mockdrconsumer; + +import java.util.List; + +import net.solarnetwork.node.settings.SettingSpecifier; +import net.solarnetwork.node.settings.SettingSpecifierProvider; +import net.solarnetwork.node.settings.support.BasicTextFieldSettingSpecifier; +import net.solarnetwork.node.support.DatumDataSourceSupport; + +public class MockDRDeviceSettings extends DatumDataSourceSupport implements SettingSpecifierProvider { + + // default values + private String sourceId = "Mock DR Device"; + private Integer minwatts = 0; + private Integer maxwatts = 10; + private Integer energycost = 1; + private Integer watts = 0; + private String drsource; + + @Override + public String getSettingUID() { + return "net.solarnetwork.node.demandresponse.mockdevice"; + } + + @Override + public String getDisplayName() { + return "Mock DR Device"; + } + + @Override + public List getSettingSpecifiers() { + MockDRDeviceSettings defaults = new MockDRDeviceSettings(); + List results = super.getIdentifiableSettingSpecifiers(); + + // user enters text + results.add(new BasicTextFieldSettingSpecifier("sourceId", defaults.sourceId)); + results.add(new BasicTextFieldSettingSpecifier("minwatts", defaults.minwatts.toString())); + results.add(new BasicTextFieldSettingSpecifier("maxwatts", defaults.maxwatts.toString())); + results.add(new BasicTextFieldSettingSpecifier("energycost", defaults.energycost.toString())); + results.add(new BasicTextFieldSettingSpecifier("drsource", defaults.drsource)); + return results; + } + + public Integer getMinwatts() { + return minwatts; + } + + public void setMinwatts(Integer minwatts) { + if (watts < minwatts) { + watts = minwatts; + } + this.minwatts = minwatts; + } + + public Integer getMaxwatts() { + return maxwatts; + } + + public void setMaxwatts(Integer maxwatts) { + if (watts > maxwatts) { + watts = maxwatts; + } + this.maxwatts = maxwatts; + } + + public Integer getEnergycost() { + return energycost; + } + + public void setEnergycost(Integer energycost) { + this.energycost = energycost; + } + + // This is the sourceID of the DRAnouncer to which the device will follow + public String getDrsource() { + return drsource; + } + + public void setDrsource(String drsource) { + this.drsource = drsource; + } + + // protected as this is not set via the settings page but instead via demand + // response + protected void setWatts(Integer watts) { + this.watts = watts; + } + + public Integer getWatts() { + return watts; + } +} From f7b0a38a76d40ed1df7e849e724d51db20928438 Mon Sep 17 00:00:00 2001 From: SolarNet Developer Date: Fri, 26 Jan 2018 01:16:05 +0000 Subject: [PATCH 2/3] removed print statement --- .../node/demandresponse/dretargetcost/DRETargetCost.java | 1 - 1 file changed, 1 deletion(-) diff --git a/net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/DRETargetCost.java b/net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/DRETargetCost.java index c3eb4c00a..0c429a5d2 100644 --- a/net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/DRETargetCost.java +++ b/net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/DRETargetCost.java @@ -172,7 +172,6 @@ public int compare(Object[] o1, Object[] o2) { // if true we need to increase demand } else if (totalCost < settings.getDrtargetCost()) { - System.out.println("Debug should be here"); // this time we start with the cheapest devices (my reasoning is // that these devices most likely have room to power on more) so we // can acheive nessisary requirements with little number of From 6720a1a53de85d6fec48df0d52a9e666b2a059a0 Mon Sep 17 00:00:00 2001 From: SolarNet Developer Date: Fri, 26 Jan 2018 01:44:24 +0000 Subject: [PATCH 3/3] removed unused class, debug print statment and a few TODO comments that have been fixed --- .../dresimplestrategy/DRESimpleStrategy.java | 1 - .../DRESimpleStrategyDatumDataSource.java | 2 - .../dretargetcost/DRETargetCost.java | 8 --- .../dretargetcost/MinimumDRStrategy.java | 57 ------------------- .../mockdrconsumer/MockDRConsumer.java | 3 - .../MockDRConsumerDatumDataSource.java | 1 - 6 files changed, 72 deletions(-) delete mode 100644 net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/MinimumDRStrategy.java diff --git a/net.solarnetwork.node.demandresponse.dresimplestrategy/src/net/solarnetwork/node/demandresponse/dresimplestrategy/DRESimpleStrategy.java b/net.solarnetwork.node.demandresponse.dresimplestrategy/src/net/solarnetwork/node/demandresponse/dresimplestrategy/DRESimpleStrategy.java index 8ed66fbf1..36d62d3d7 100644 --- a/net.solarnetwork.node.demandresponse.dresimplestrategy/src/net/solarnetwork/node/demandresponse/dresimplestrategy/DRESimpleStrategy.java +++ b/net.solarnetwork.node.demandresponse.dresimplestrategy/src/net/solarnetwork/node/demandresponse/dresimplestrategy/DRESimpleStrategy.java @@ -40,7 +40,6 @@ public void setSettings(DRESimpleStrategyDatumDataSource settings) { } - // TODO give this method a massive refactoring protected void drupdate() { numdrdevices = 0; for (FeedbackInstructionHandler handler : feedbackInstructionHandlers) { diff --git a/net.solarnetwork.node.demandresponse.dresimplestrategy/src/net/solarnetwork/node/demandresponse/dresimplestrategy/DRESimpleStrategyDatumDataSource.java b/net.solarnetwork.node.demandresponse.dresimplestrategy/src/net/solarnetwork/node/demandresponse/dresimplestrategy/DRESimpleStrategyDatumDataSource.java index bfba7a50f..223f705a6 100644 --- a/net.solarnetwork.node.demandresponse.dresimplestrategy/src/net/solarnetwork/node/demandresponse/dresimplestrategy/DRESimpleStrategyDatumDataSource.java +++ b/net.solarnetwork.node.demandresponse.dresimplestrategy/src/net/solarnetwork/node/demandresponse/dresimplestrategy/DRESimpleStrategyDatumDataSource.java @@ -17,8 +17,6 @@ public class DRESimpleStrategyDatumDataSource extends DatumDataSourceSupport private DRESimpleStrategy linkedInstance; private String batteryMode = "Idle"; - // TODO refactor how linkinstance works - @Override public String getSettingUID() { return "net.solarnetwork.node.demandresponse.dresimplestrategy"; diff --git a/net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/DRETargetCost.java b/net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/DRETargetCost.java index 0c429a5d2..60c3ab0bb 100644 --- a/net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/DRETargetCost.java +++ b/net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/DRETargetCost.java @@ -102,8 +102,6 @@ protected void drupdate() { Integer wattValue = DRSupportTools.readWatts(params); Integer costValue = DRSupportTools.readEnergyDepreciationCost(params); - // TODO double check you have done your maths correctly to factor in - // the convention of price being in KWh but using watts costArray[i][0] = (costValue + newPrice) * wattValue; costArray[i][1] = d; } @@ -148,8 +146,6 @@ public int compare(Object[] o1, Object[] o2) { Double appliedenergyReduction = (wattValue - energyReduction > minValue) ? energyReduction : wattValue - minValue; - System.out.println("reduceAmount" + appliedenergyReduction); - // Im annoyed by this instruction because it is only reduce // and not gain sendShedInstruction(d, appliedenergyReduction); @@ -160,10 +156,6 @@ public int compare(Object[] o1, Object[] o2) { break; } else { // update the cost for the next devices to calcuate with - // TODO ensure that if we have changed batterys this is - // still effective - // One idea is to have a method to recalcuate the entire - // cost all over again totalCost -= appliedenergyReduction * (energyCost + settings.getEnergyCost()); } diff --git a/net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/MinimumDRStrategy.java b/net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/MinimumDRStrategy.java deleted file mode 100644 index 8740dfc3d..000000000 --- a/net.solarnetwork.node.demandresponse.dretargetcost/src/net/solarnetwork/node/demandresponse/dretargetcost/MinimumDRStrategy.java +++ /dev/null @@ -1,57 +0,0 @@ -package net.solarnetwork.node.demandresponse.dretargetcost; - -import java.util.Date; -import java.util.List; -import java.util.Map; - -import net.solarnetwork.node.reactor.FeedbackInstructionHandler; -import net.solarnetwork.node.reactor.Instruction; -import net.solarnetwork.node.reactor.InstructionHandler; -import net.solarnetwork.node.reactor.support.BasicInstruction; - -/** - * another DRStrategy this one just powers off as much as it can. Using this to - * test being able to change strategys - * - * @author robert - * - */ -public class MinimumDRStrategy { - private List handlers; - - public void drupdate() { - for (FeedbackInstructionHandler handler : handlers) { - if (handler.handlesTopic("getDRDeviceInstance")) { - - BasicInstruction instr = new BasicInstruction("getDRDeviceInstance", new Date(), - Instruction.LOCAL_INSTRUCTION_ID, Instruction.LOCAL_INSTRUCTION_ID, null); - - // The devices want to know where the instruction came from for - // verification - // TODO work on verification - // instr.addParameter(settings.getUID(), ""); - - Map params = handler.processInstructionWithFeedback(instr).getResultParameters(); - if (DRSupportTools.isDRCapable(params)) { - Integer watts = DRSupportTools.readWatts(params); - Integer minwatts = DRSupportTools.readMinWatts(params); - if (watts > minwatts) { - Integer shedamount = watts - minwatts; - // in this strategy we just lower power of all devices - // as much as we can - instr = new BasicInstruction(InstructionHandler.TOPIC_SHED_LOAD, new Date(), - Instruction.LOCAL_INSTRUCTION_ID, Instruction.LOCAL_INSTRUCTION_ID, null); - // TODO figure out verification - instr.addParameter("temp", shedamount.toString()); - handler.processInstruction(instr); - } - } - } - } - } - - public void setHandlers(List handlers) { - this.handlers = handlers; - - } -} diff --git a/net.solarnetwork.node.demandresponse.mockdrconsumer/src/net/solarnetwork/node/demandresponse/mockdrconsumer/MockDRConsumer.java b/net.solarnetwork.node.demandresponse.mockdrconsumer/src/net/solarnetwork/node/demandresponse/mockdrconsumer/MockDRConsumer.java index 7ba2b4b09..21ccb7607 100644 --- a/net.solarnetwork.node.demandresponse.mockdrconsumer/src/net/solarnetwork/node/demandresponse/mockdrconsumer/MockDRConsumer.java +++ b/net.solarnetwork.node.demandresponse.mockdrconsumer/src/net/solarnetwork/node/demandresponse/mockdrconsumer/MockDRConsumer.java @@ -120,9 +120,6 @@ public InstructionStatus processInstructionWithFeedback(Instruction instruction) // and turn it to an int double value = Double.parseDouble(param) + 0.5;// 0.5 for // rounding - - // TODO shed load should only be able to reduce to minwatts - // BUG value = settings.getWatts() - value; if (value < settings.getMinwatts()) { settings.setWatts(settings.getMinwatts()); diff --git a/net.solarnetwork.node.demandresponse.mockdrconsumer/src/net/solarnetwork/node/demandresponse/mockdrconsumer/MockDRConsumerDatumDataSource.java b/net.solarnetwork.node.demandresponse.mockdrconsumer/src/net/solarnetwork/node/demandresponse/mockdrconsumer/MockDRConsumerDatumDataSource.java index 9409fc0a3..246b8158c 100644 --- a/net.solarnetwork.node.demandresponse.mockdrconsumer/src/net/solarnetwork/node/demandresponse/mockdrconsumer/MockDRConsumerDatumDataSource.java +++ b/net.solarnetwork.node.demandresponse.mockdrconsumer/src/net/solarnetwork/node/demandresponse/mockdrconsumer/MockDRConsumerDatumDataSource.java @@ -93,7 +93,6 @@ public Integer getWatts() { @Override public Class getDatumType() { - // TODO Auto-generated method stub return GeneralNodeACEnergyDatum.class; }