diff --git a/lib/matlabcontrol-4.1.0.jar b/lib/matlabcontrol-4.1.0.jar
new file mode 100644
index 0000000..93622af
Binary files /dev/null and b/lib/matlabcontrol-4.1.0.jar differ
diff --git a/pom.xml b/pom.xml
index 558b08c..a27d4f5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -37,7 +37,7 @@
spring-jms
${spring.version}
-
+
org.apache.activemq
@@ -94,6 +94,11 @@
commons-io
1.3.2
+
+ org.matlabcontrol
+ matlabcontrol
+ 4.1.0
+
diff --git a/src/main/java/org/powertac/samplebroker/PortfolioManagerProxy.java b/src/main/java/org/powertac/samplebroker/PortfolioManagerProxy.java
new file mode 100644
index 0000000..092b290
--- /dev/null
+++ b/src/main/java/org/powertac/samplebroker/PortfolioManagerProxy.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2012-2013 by the original author
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.powertac.samplebroker;
+
+import matlabcontrol.MatlabConnectionException;
+import matlabcontrol.MatlabInvocationException;
+import matlabcontrol.MatlabProxy;
+import matlabcontrol.MatlabProxyFactory;
+import org.apache.log4j.Logger;
+import org.joda.time.Instant;
+import org.powertac.common.*;
+import org.powertac.common.msg.*;
+import org.powertac.common.repo.CustomerRepo;
+import org.powertac.common.repo.TariffRepo;
+import org.powertac.common.repo.TimeslotRepo;
+import org.powertac.samplebroker.core.BrokerPropertiesService;
+import org.powertac.samplebroker.interfaces.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Arrays;
+
+
+/**
+ * Handles portfolio-management responsibilities for the broker. This
+ * includes composing and offering tariffs, keeping track of customers and their
+ * usage, monitoring tariff offerings from competing brokers.
+ *
+ * A more complete broker implementation might split this class into two or
+ * more classes; the keys are to decide which messages each class handles,
+ * what each class does on the activate() method, and what data needs to be
+ * managed and shared.
+ *
+ * @author John Collins
+ */
+@Service // Spring creates a single instance at startup
+public class PortfolioManagerProxy
+ implements PortfolioManager, Initializable, Activatable
+{
+ static private Logger log = Logger.getLogger(PortfolioManagerProxy.class);
+
+ // Spring fills in Autowired dependencies through a naming convention
+ @Autowired
+ private BrokerPropertiesService propertiesService;
+
+ @Autowired
+ private TimeslotRepo timeslotRepo;
+
+ @Autowired
+ private TariffRepo tariffRepo;
+
+ @Autowired
+ private CustomerRepo customerRepo;
+
+ @Autowired
+ private MarketManager marketManager;
+
+ @Autowired
+ private TimeService timeService;
+
+ private MatlabProxy proxy;
+
+ /**
+ * Default constructor registers for messages, must be called after
+ * message router is available.
+ */
+ public PortfolioManagerProxy ()
+ {
+ super();
+
+ try {
+ proxy = new MatlabProxyFactory().getProxy();
+ proxy.eval("cd src/main/matlab/portfoliomanager;");
+ }
+ catch (MatlabConnectionException e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ catch (MatlabInvocationException e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ /**
+ * Per-game initialization. Configures parameters and registers
+ * message handlers.
+ */
+ @Override // from Initializable
+ public void initialize (BrokerContext context)
+ {
+ propertiesService.configureMe(this);
+
+ try {
+ proxy.feval("mInit", context, timeslotRepo, tariffRepo,
+ customerRepo, marketManager, timeService, log, null);
+ }
+ catch (MatlabInvocationException mie) {
+ mie.printStackTrace();
+ }
+ }
+
+ // -------------- data access ------------------
+ /**
+ * Returns total usage for a given timeslot (represented as a simple index).
+ */
+ @Override
+ public double collectUsage (int index)
+ {
+ double result = 0.0;
+ try {
+ Object[] returnArguments = proxy.returningFeval("collectUsage", 1, index);
+ result = ((double[]) returnArguments[0])[0];
+
+ if (Double.isNaN(result)) {
+ result = 0.0;
+ }
+ }
+ catch (MatlabInvocationException mie) {
+ mie.printStackTrace();
+ }
+
+ return -result; // convert to needed energy account balance
+ }
+
+ // -------------- Message handlers -------------------
+
+ /**
+ * Handles CustomerBootstrapData by populating the customer model
+ * corresponding to the given customer and power type. This gives the
+ * broker a running start.
+ */
+ public void handleMessage (CustomerBootstrapData cbd)
+ {
+ try {
+ proxy.feval("msgCustomerBootstrapData", cbd);
+ }
+ catch (MatlabInvocationException mie) {
+ mie.printStackTrace();
+ }
+ }
+
+ /**
+ * Handles a TariffSpecification. These are sent by the server when new tariffs are
+ * published. If it's not ours, then it's a competitor's tariff. We keep track of
+ * competing tariffs locally, and we also store them in the tariffRepo.
+ */
+ public void handleMessage (TariffSpecification spec)
+ {
+ try {
+ proxy.feval("msgTariffSpecification", spec);
+ }
+ catch (MatlabInvocationException mie) {
+ mie.printStackTrace();
+ }
+ }
+
+ /**
+ * Handles a TariffStatus message. This should do something when the status
+ * is not SUCCESS.
+ */
+ public void handleMessage (TariffStatus ts)
+ {
+ try {
+ proxy.feval("msgTariffStatus", ts);
+ }
+ catch (MatlabInvocationException mie) {
+ mie.printStackTrace();
+ }
+ }
+
+ /**
+ * Handles a TariffTransaction. We only care about certain types: PRODUCE,
+ * CONSUME, SIGNUP, and WITHDRAW.
+ */
+ public void handleMessage (TariffTransaction ttx)
+ {
+ try {
+ proxy.feval("msgTariffTransaction", ttx);
+ }
+ catch (MatlabInvocationException mie) {
+ mie.printStackTrace();
+ }
+ }
+
+ /**
+ * Handles a TariffRevoke message from the server, indicating that some
+ * tariff has been revoked.
+ */
+ public void handleMessage (TariffRevoke tr)
+ {
+ try {
+ proxy.feval("msgTariffRevoke", tr);
+ }
+ catch (MatlabInvocationException mie) {
+ mie.printStackTrace();
+ }
+ }
+
+ /**
+ * Handles a BalancingControlEvent, sent when a BalancingOrder is
+ * exercised by the DU.
+ */
+ public void handleMessage (BalancingControlEvent bce)
+ {
+ try {
+ proxy.feval("msgBalancingControlEvent", bce);
+ }
+ catch (MatlabInvocationException mie) {
+ mie.printStackTrace();
+ }
+ }
+
+ // --------------- activation -----------------
+
+ /**
+ * Called after TimeslotComplete msg received. Note that activation order
+ * among modules is non-deterministic.
+ */
+ @Override // from Activatable
+ public void activate (int timeslotIndex)
+ {
+ try {
+ proxy.feval("activate", timeslotIndex);
+ }
+ catch (MatlabInvocationException mie) {
+ mie.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/org/powertac/samplebroker/PortfolioManagerService.java b/src/main/java/org/powertac/samplebroker/PortfolioManagerService.java
deleted file mode 100644
index 6b9bc10..0000000
--- a/src/main/java/org/powertac/samplebroker/PortfolioManagerService.java
+++ /dev/null
@@ -1,654 +0,0 @@
-/*
- * Copyright (c) 2012-2013 by the original author
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.powertac.samplebroker;
-
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-import org.joda.time.Instant;
-import org.powertac.common.Broker;
-import org.powertac.common.Competition;
-import org.powertac.common.CustomerInfo;
-import org.powertac.common.IdGenerator;
-import org.powertac.common.Rate;
-import org.powertac.common.TariffSpecification;
-import org.powertac.common.TariffTransaction;
-import org.powertac.common.TimeService;
-import org.powertac.common.config.ConfigurableValue;
-import org.powertac.common.enumerations.PowerType;
-import org.powertac.common.msg.BalancingControlEvent;
-import org.powertac.common.msg.BalancingOrder;
-import org.powertac.common.msg.CustomerBootstrapData;
-import org.powertac.common.msg.TariffRevoke;
-import org.powertac.common.msg.TariffStatus;
-import org.powertac.common.repo.CustomerRepo;
-import org.powertac.common.repo.TariffRepo;
-import org.powertac.common.repo.TimeslotRepo;
-import org.powertac.samplebroker.core.BrokerPropertiesService;
-import org.powertac.samplebroker.interfaces.Activatable;
-import org.powertac.samplebroker.interfaces.BrokerContext;
-import org.powertac.samplebroker.interfaces.Initializable;
-import org.powertac.samplebroker.interfaces.MarketManager;
-import org.powertac.samplebroker.interfaces.PortfolioManager;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
-/**
- * Handles portfolio-management responsibilities for the broker. This
- * includes composing and offering tariffs, keeping track of customers and their
- * usage, monitoring tariff offerings from competing brokers.
- *
- * A more complete broker implementation might split this class into two or
- * more classes; the keys are to decide which messages each class handles,
- * what each class does on the activate() method, and what data needs to be
- * managed and shared.
- *
- * @author John Collins
- */
-@Service // Spring creates a single instance at startup
-public class PortfolioManagerService
-implements PortfolioManager, Initializable, Activatable
-{
- static private Logger log = Logger.getLogger(PortfolioManagerService.class);
-
- private BrokerContext brokerContext; // master
-
- // Spring fills in Autowired dependencies through a naming convention
- @Autowired
- private BrokerPropertiesService propertiesService;
-
- @Autowired
- private TimeslotRepo timeslotRepo;
-
- @Autowired
- private TariffRepo tariffRepo;
-
- @Autowired
- private CustomerRepo customerRepo;
-
- @Autowired
- private MarketManager marketManager;
-
- @Autowired
- private TimeService timeService;
-
- // ---- Portfolio records -----
- // Customer records indexed by power type and by tariff. Note that the
- // CustomerRecord instances are NOT shared between these structures, because
- // we need to keep track of subscriptions by tariff.
- private HashMap> customerProfiles;
- private HashMap> customerSubscriptions;
- private HashMap> competingTariffs;
-
- // Configurable parameters for tariff composition
- // Override defaults in src/main/resources/config/broker.config
- // or in top-level config file
- @ConfigurableValue(valueType = "Double",
- description = "target profit margin")
- private double defaultMargin = 0.5;
-
- @ConfigurableValue(valueType = "Double",
- description = "Fixed cost/kWh")
- private double fixedPerKwh = -0.06;
-
- @ConfigurableValue(valueType = "Double",
- description = "Default daily meter charge")
- private double defaultPeriodicPayment = -1.0;
-
- /**
- * Default constructor registers for messages, must be called after
- * message router is available.
- */
- public PortfolioManagerService ()
- {
- super();
- }
-
- /**
- * Per-game initialization. Configures parameters and registers
- * message handlers.
- */
- @Override // from Initializable
-// @SuppressWarnings("unchecked")
- public void initialize (BrokerContext context)
- {
- this.brokerContext = context;
- propertiesService.configureMe(this);
- customerProfiles = new HashMap>();
- customerSubscriptions = new HashMap>();
- competingTariffs = new HashMap>();
- }
-
- // -------------- data access ------------------
-
- /**
- * Returns the CustomerRecord for the given type and customer, creating it
- * if necessary.
- */
- CustomerRecord getCustomerRecordByPowerType (PowerType type,
- CustomerInfo customer)
- {
- HashMap customerMap =
- customerProfiles.get(type);
- if (customerMap == null) {
- customerMap = new HashMap();
- customerProfiles.put(type, customerMap);
- }
- CustomerRecord record = customerMap.get(customer);
- if (record == null) {
- record = new CustomerRecord(customer);
- customerMap.put(customer, record);
- }
- return record;
- }
-
- /**
- * Returns the customer record for the given tariff spec and customer,
- * creating it if necessary.
- */
- CustomerRecord getCustomerRecordByTariff (TariffSpecification spec,
- CustomerInfo customer)
- {
- HashMap customerMap =
- customerSubscriptions.get(spec);
- if (customerMap == null) {
- customerMap = new HashMap();
- customerSubscriptions.put(spec, customerMap);
- }
- CustomerRecord record = customerMap.get(customer);
- if (record == null) {
- // seed with the generic record for this customer
- record =
- new CustomerRecord(getCustomerRecordByPowerType(spec.getPowerType(),
- customer));
- customerMap.put(customer, record);
- }
- return record;
- }
-
- /**
- * Finds the list of competing tariffs for the given PowerType.
- */
- List getCompetingTariffs (PowerType powerType)
- {
- List result = competingTariffs.get(powerType);
- if (result == null) {
- result = new ArrayList();
- competingTariffs.put(powerType, result);
- }
- return result;
- }
-
- /**
- * Adds a new competing tariff to the list.
- */
- private void addCompetingTariff (TariffSpecification spec)
- {
- getCompetingTariffs(spec.getPowerType()).add(spec);
- }
-
- /**
- * Returns total usage for a given timeslot (represented as a simple index).
- */
- @Override
- public double collectUsage (int index)
- {
- double result = 0.0;
- for (HashMap customerMap : customerSubscriptions.values()) {
- for (CustomerRecord record : customerMap.values()) {
- result += record.getUsage(index);
- }
- }
- return -result; // convert to needed energy account balance
- }
-
- // -------------- Message handlers -------------------
- /**
- * Handles CustomerBootstrapData by populating the customer model
- * corresponding to the given customer and power type. This gives the
- * broker a running start.
- */
- public void handleMessage (CustomerBootstrapData cbd)
- {
- CustomerInfo customer =
- customerRepo.findByNameAndPowerType(cbd.getCustomerName(),
- cbd.getPowerType());
- CustomerRecord record = getCustomerRecordByPowerType(cbd.getPowerType(), customer);
- int offset = (timeslotRepo.currentTimeslot().getSerialNumber()
- - cbd.getNetUsage().length);
- int subs = record.subscribedPopulation;
- record.subscribedPopulation = customer.getPopulation();
- for (int i = 0; i < cbd.getNetUsage().length; i++) {
- record.produceConsume(cbd.getNetUsage()[i], i);
- }
- record.subscribedPopulation = subs;
- }
-
- /**
- * Handles a TariffSpecification. These are sent by the server when new tariffs are
- * published. If it's not ours, then it's a competitor's tariff. We keep track of
- * competing tariffs locally, and we also store them in the tariffRepo.
- */
- public void handleMessage (TariffSpecification spec)
- {
- Broker theBroker = spec.getBroker();
- if (brokerContext.getBrokerUsername().equals(theBroker.getUsername())) {
- if (theBroker != brokerContext)
- // strange bug, seems harmless for now
- log.info("Resolution failed for broker " + theBroker.getUsername());
- // if it's ours, just log it, because we already put it in the repo
- TariffSpecification original =
- tariffRepo.findSpecificationById(spec.getId());
- if (null == original)
- log.error("Spec " + spec.getId() + " not in local repo");
- log.info("published " + spec);
- }
- else {
- // otherwise, keep track of competing tariffs, and record in the repo
- addCompetingTariff(spec);
- tariffRepo.addSpecification(spec);
- }
- }
-
- /**
- * Handles a TariffStatus message. This should do something when the status
- * is not SUCCESS.
- */
- public void handleMessage (TariffStatus ts)
- {
- log.info("TariffStatus: " + ts.getStatus());
- }
-
- /**
- * Handles a TariffTransaction. We only care about certain types: PRODUCE,
- * CONSUME, SIGNUP, and WITHDRAW.
- */
- public void handleMessage(TariffTransaction ttx)
- {
- // make sure we have this tariff
- TariffSpecification newSpec = ttx.getTariffSpec();
- if (newSpec == null) {
- log.error("TariffTransaction type=" + ttx.getTxType()
- + " for unknown spec");
- }
- else {
- TariffSpecification oldSpec =
- tariffRepo.findSpecificationById(newSpec.getId());
- if (oldSpec != newSpec) {
- log.error("Incoming spec " + newSpec.getId() + " not matched in repo");
- }
- }
- TariffTransaction.Type txType = ttx.getTxType();
- CustomerRecord record = getCustomerRecordByTariff(ttx.getTariffSpec(),
- ttx.getCustomerInfo());
-
- if (TariffTransaction.Type.SIGNUP == txType) {
- // keep track of customer counts
- record.signup(ttx.getCustomerCount());
- }
- else if (TariffTransaction.Type.WITHDRAW == txType) {
- // customers presumably found a better deal
- record.withdraw(ttx.getCustomerCount());
- }
- else if (TariffTransaction.Type.PRODUCE == txType) {
- // if ttx count and subscribe population don't match, it will be hard
- // to estimate per-individual production
- if (ttx.getCustomerCount() != record.subscribedPopulation) {
- log.warn("production by subset " + ttx.getCustomerCount() +
- " of subscribed population " + record.subscribedPopulation);
- }
- record.produceConsume(ttx.getKWh(), ttx.getPostedTime());
- }
- else if (TariffTransaction.Type.CONSUME == txType) {
- if (ttx.getCustomerCount() != record.subscribedPopulation) {
- log.warn("consumption by subset " + ttx.getCustomerCount() +
- " of subscribed population " + record.subscribedPopulation);
- }
- record.produceConsume(ttx.getKWh(), ttx.getPostedTime());
- }
- }
-
- /**
- * Handles a TariffRevoke message from the server, indicating that some
- * tariff has been revoked.
- */
- public void handleMessage (TariffRevoke tr)
- {
- Broker source = tr.getBroker();
- log.info("Revoke tariff " + tr.getTariffId()
- + " from " + tr.getBroker().getUsername());
- // if it's from some other broker, we need to remove it from the
- // tariffRepo, and from the competingTariffs list
- if (!(source.getUsername().equals(brokerContext.getBrokerUsername()))) {
- log.info("clear out competing tariff");
- TariffSpecification original =
- tariffRepo.findSpecificationById(tr.getTariffId());
- if (null == original) {
- log.warn("Original tariff " + tr.getTariffId() + " not found");
- return;
- }
- tariffRepo.removeSpecification(original.getId());
- List candidates =
- competingTariffs.get(original.getPowerType());
- if (null == candidates) {
- log.warn("Candidate list is null");
- return;
- }
- candidates.remove(original);
- }
- }
-
- /**
- * Handles a BalancingControlEvent, sent when a BalancingOrder is
- * exercised by the DU.
- */
- public void handleMessage (BalancingControlEvent bce)
- {
- log.info("BalancingControlEvent " + bce.getKwh());
- }
-
- // --------------- activation -----------------
- /**
- * Called after TimeslotComplete msg received. Note that activation order
- * among modules is non-deterministic.
- */
- @Override // from Activatable
- public void activate (int timeslotIndex)
- {
- if (customerSubscriptions.size() == 0) {
- // we (most likely) have no tariffs
- createInitialTariffs();
- }
- else {
- // we have some, are they good enough?
- improveTariffs();
- }
- }
-
- // Creates initial tariffs for the main power types. These are simple
- // fixed-rate two-part tariffs that give the broker a fixed margin.
- private void createInitialTariffs ()
- {
- // remember that market prices are per mwh, but tariffs are by kwh
- double marketPrice = marketManager.getMeanMarketPrice() / 1000.0;
- // for each power type representing a customer population,
- // create a tariff that's better than what's available
- for (PowerType pt : customerProfiles.keySet()) {
- // we'll just do fixed-rate tariffs for now
- double rateValue;
- if (pt.isConsumption())
- rateValue = ((marketPrice + fixedPerKwh) * (1.0 + defaultMargin));
- else
- //rateValue = (-1.0 * marketPrice / (1.0 + defaultMargin));
- rateValue = -2.0 * marketPrice;
- if (pt.isInterruptible())
- rateValue *= 0.7; // Magic number!! price break for interruptible
- TariffSpecification spec =
- new TariffSpecification(brokerContext.getBroker(), pt)
- .withPeriodicPayment(defaultPeriodicPayment);
- Rate rate = new Rate().withValue(rateValue);
- if (pt.isInterruptible()) {
- // set max curtailment
- rate.withMaxCurtailment(0.1);
- }
- spec.addRate(rate);
- customerSubscriptions.put(spec, new HashMap());
- tariffRepo.addSpecification(spec);
- brokerContext.sendMessage(spec);
- }
- }
-
- // Checks to see whether our tariffs need fine-tuning
- private void improveTariffs()
- {
- // quick magic-number hack to inject a balancing order
- int timeslotIndex = timeslotRepo.currentTimeslot().getSerialNumber();
- if (371 == timeslotIndex) {
- for (TariffSpecification spec :
- tariffRepo.findTariffSpecificationsByBroker(brokerContext.getBroker())) {
- if (PowerType.INTERRUPTIBLE_CONSUMPTION == spec.getPowerType()) {
- BalancingOrder order = new BalancingOrder(brokerContext.getBroker(),
- spec,
- 0.5,
- spec.getRates().get(0).getMinValue() * 0.9);
- brokerContext.sendMessage(order);
- }
- }
- }
- // magic-number hack to supersede a tariff
- if (380 == timeslotIndex) {
- // find the existing CONSUMPTION tariff
- TariffSpecification oldc = null;
- List candidates =
- tariffRepo.findTariffSpecificationsByBroker(brokerContext.getBroker());
- if (null == candidates || 0 == candidates.size())
- log.error("No tariffs found for broker");
- else {
- // oldc = candidates.get(0);
- for (TariffSpecification candidate: candidates) {
- if (candidate.getPowerType() == PowerType.CONSUMPTION) {
- oldc = candidate;
- break;
- }
- }
- if (null == oldc) {
- log.warn("No CONSUMPTION tariffs found");
- }
- else {
- double rateValue = oldc.getRates().get(0).getValue();
- // create a new CONSUMPTION tariff
- TariffSpecification spec =
- new TariffSpecification(brokerContext.getBroker(),
- PowerType.CONSUMPTION)
- .withPeriodicPayment(defaultPeriodicPayment * 1.1);
- Rate rate = new Rate().withValue(rateValue);
- spec.addRate(rate);
- if (null != oldc)
- spec.addSupersedes(oldc.getId());
- mungId(spec, 6);
- tariffRepo.addSpecification(spec);
- brokerContext.sendMessage(spec);
- // revoke the old one
- TariffRevoke revoke =
- new TariffRevoke(brokerContext.getBroker(), oldc);
- brokerContext.sendMessage(revoke);
- }
- }
- }
- }
-
- private void mungId (TariffSpecification spec, int i)
- {
- long id = spec.getId();
- long baseId =
- id - IdGenerator.extractPrefix(id) * IdGenerator.getMultiplier();
- Field idField = findIdField(spec.getClass());
- try {
- idField.setAccessible(true);
- idField.setLong(spec, baseId + i * IdGenerator.getMultiplier());
- }
- catch (Exception e) {
- log.error(e.toString());
- }
- }
-
- private Field findIdField (Class> clazz)
- {
- try {
- Field idField = clazz.getDeclaredField("id");
- return idField;
- }
- catch (NoSuchFieldException e) {
- Class> superclass = clazz.getSuperclass();
- if (null == superclass) {
- return null;
- }
- return findIdField(superclass);
- }
- catch (SecurityException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- return null;
- }
- }
-
- // ------------- test-support methods ----------------
- double getUsageForCustomer (CustomerInfo customer,
- TariffSpecification tariffSpec,
- int index)
- {
- CustomerRecord record = getCustomerRecordByTariff(tariffSpec, customer);
- return record.getUsage(index);
- }
-
- // test-support method
- HashMap getRawUsageForCustomer (CustomerInfo customer)
- {
- HashMap result = new HashMap();
- for (PowerType type : customerProfiles.keySet()) {
- CustomerRecord record = customerProfiles.get(type).get(customer);
- if (record != null) {
- result.put(type, record.usage);
- }
- }
- return result;
- }
-
- // test-support method
- HashMap getCustomerCounts()
- {
- HashMap result = new HashMap();
- for (TariffSpecification spec : customerSubscriptions.keySet()) {
- HashMap customerMap = customerSubscriptions.get(spec);
- for (CustomerRecord record : customerMap.values()) {
- result.put(record.customer.getName() + spec.getPowerType(),
- record.subscribedPopulation);
- }
- }
- return result;
- }
-
- //-------------------- Customer-model recording ---------------------
- /**
- * Keeps track of customer status and usage. Usage is stored
- * per-customer-unit, but reported as the product of the per-customer
- * quantity and the subscribed population. This allows the broker to use
- * historical usage data as the subscribed population shifts.
- */
- class CustomerRecord
- {
- CustomerInfo customer;
- int subscribedPopulation = 0;
- double[] usage;
- double alpha = 0.3;
-
- /**
- * Creates an empty record
- */
- CustomerRecord (CustomerInfo customer)
- {
- super();
- this.customer = customer;
- this.usage = new double[brokerContext.getUsageRecordLength()];
- }
-
- CustomerRecord (CustomerRecord oldRecord)
- {
- super();
- this.customer = oldRecord.customer;
- this.usage = Arrays.copyOf(oldRecord.usage, brokerContext.getUsageRecordLength());
- }
-
- // Returns the CustomerInfo for this record
- CustomerInfo getCustomerInfo ()
- {
- return customer;
- }
-
- // Adds new individuals to the count
- void signup (int population)
- {
- subscribedPopulation = Math.min(customer.getPopulation(),
- subscribedPopulation + population);
- }
-
- // Removes individuals from the count
- void withdraw (int population)
- {
- subscribedPopulation -= population;
- }
-
- // Customer produces or consumes power. We assume the kwh value is negative
- // for production, positive for consumption
- void produceConsume (double kwh, Instant when)
- {
- int index = getIndex(when);
- produceConsume(kwh, index);
- }
-
- // store profile data at the given index
- void produceConsume (double kwh, int rawIndex)
- {
- int index = getIndex(rawIndex);
- double kwhPerCustomer = 0.0;
- if (subscribedPopulation > 0) {
- kwhPerCustomer = kwh / (double)subscribedPopulation;
- }
- double oldUsage = usage[index];
- if (oldUsage == 0.0) {
- // assume this is the first time
- usage[index] = kwhPerCustomer;
- }
- else {
- // exponential smoothing
- usage[index] = alpha * kwhPerCustomer + (1.0 - alpha) * oldUsage;
- }
- log.debug("consume " + kwh + " at " + index +
- ", customer " + customer.getName());
- }
-
- double getUsage (int index)
- {
- if (index < 0) {
- log.warn("usage requested for negative index " + index);
- index = 0;
- }
- return (usage[getIndex(index)] * (double)subscribedPopulation);
- }
-
- // we assume here that timeslot index always matches the number of
- // timeslots that have passed since the beginning of the simulation.
- int getIndex (Instant when)
- {
- int result = (int)((when.getMillis() - timeService.getBase()) /
- (Competition.currentCompetition().getTimeslotDuration()));
- return result;
- }
-
- private int getIndex (int rawIndex)
- {
- return rawIndex % usage.length;
- }
- }
-}
diff --git a/src/main/java/org/powertac/samplebroker/core/BrokerMain.java b/src/main/java/org/powertac/samplebroker/core/BrokerMain.java
index 33fa26f..c344235 100644
--- a/src/main/java/org/powertac/samplebroker/core/BrokerMain.java
+++ b/src/main/java/org/powertac/samplebroker/core/BrokerMain.java
@@ -15,18 +15,12 @@
*/
package org.powertac.samplebroker.core;
-//import org.apache.log4j.Logger;
-import org.springframework.context.support.AbstractApplicationContext;
-import org.springframework.context.support.ClassPathXmlApplicationContext;
-
/**
* This is the top level of the Power TAC server.
* @author John Collins
*/
-public class BrokerMain
+public class BrokerMain implements Runnable
{
- //static private Logger log = Logger.getLogger(BrokerMain.class);
-
/**
* Sets up the broker. Single command-line arg is the username
*/
@@ -34,8 +28,26 @@ public static void main (String[] args)
{
BrokerRunner runner = new BrokerRunner();
runner.processCmdLine(args);
-
+
// if we get here, it's time to exit
System.exit(0);
}
+
+ private static String[] mainArgs;
+
+ public static void mainMatlab (String[] args)
+ {
+ // This is some dirty hacking because Matlab doesn't load jars properly
+ ClassPathHacker.loadJarDynamically();
+
+ mainArgs = args;
+ Thread t = new Thread( new BrokerMain() );
+ t.start();
+ }
+
+ public void run()
+ {
+ BrokerRunner runner = new BrokerRunner();
+ runner.processCmdLine(mainArgs);
+ }
}
diff --git a/src/main/java/org/powertac/samplebroker/core/BrokerPropertiesService.java b/src/main/java/org/powertac/samplebroker/core/BrokerPropertiesService.java
index fbaf988..0696a41 100644
--- a/src/main/java/org/powertac/samplebroker/core/BrokerPropertiesService.java
+++ b/src/main/java/org/powertac/samplebroker/core/BrokerPropertiesService.java
@@ -15,11 +15,6 @@
*/
package org.powertac.samplebroker.core;
-import java.io.File;
-import java.io.IOException;
-import java.net.URL;
-import java.util.Collection;
-
import org.apache.commons.configuration.CompositeConfiguration;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
@@ -33,6 +28,10 @@
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+
/**
* @author jcollins
*/
@@ -199,6 +198,11 @@ public void setProperty (String key, Object value)
private boolean validXmlResource (Resource xml)
{
try {
+ xml.getInputStream();
+ return true;
+
+ /* TODO Doesn't seem to work with files inside a jar?
+ Jar is added to the classpath, and then the
String path = xml.getFile().getPath();
for (String regex : excludedPaths) {
if (path.matches(regex)) {
@@ -206,6 +210,7 @@ private boolean validXmlResource (Resource xml)
}
}
return true;
+ */
}
catch (IOException e) {
log.error("Should not happen: " + e.toString());
diff --git a/src/main/java/org/powertac/samplebroker/core/BrokerRunner.java b/src/main/java/org/powertac/samplebroker/core/BrokerRunner.java
index 0179f6a..27143a2 100644
--- a/src/main/java/org/powertac/samplebroker/core/BrokerRunner.java
+++ b/src/main/java/org/powertac/samplebroker/core/BrokerRunner.java
@@ -15,21 +15,22 @@
*/
package org.powertac.samplebroker.core;
-import java.io.File;
-import java.util.Date;
-import java.util.Enumeration;
-
import joptsimple.OptionException;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;
-
import org.apache.log4j.Appender;
import org.apache.log4j.FileAppender;
+import org.apache.log4j.Level;
import org.apache.log4j.Logger;
+import org.apache.log4j.PatternLayout;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
+import java.io.File;
+import java.util.Date;
+import java.util.Enumeration;
+
/**
* Multi-session broker runner. The Spring context is re-built for each
* session.
@@ -115,7 +116,7 @@ else if (options.has(repeatHoursOption)) {
// Re-open the logfiles
reopenLogs(counter);
-
+
// initialize and run
if (null == context) {
context = new ClassPathXmlApplicationContext("broker.xml");
@@ -126,7 +127,7 @@ else if (options.has(repeatHoursOption)) {
}
// get the broker reference and delegate the rest
context.registerShutdownHook();
- broker = (PowerTacBroker)context.getBeansOfType(PowerTacBroker.class).values().toArray()[0];
+ broker = (PowerTacBroker) context.getBeansOfType(PowerTacBroker.class).values().toArray()[0];
System.out.println("Starting session " + counter);
broker.startSession(configFile, jmsUrl, noNtp, queueName, serverQueue, end);
if (null != repeatCount)
@@ -144,17 +145,53 @@ private void reopenLogs(int counter)
Logger root = Logger.getRootLogger();
@SuppressWarnings("unchecked")
Enumeration rootAppenders = root.getAllAppenders();
- FileAppender logOutput = (FileAppender) rootAppenders.nextElement();
- // assume there's only the one, and that it's a file appender
- logOutput.setFile("log/broker" + counter + ".trace");
- logOutput.activateOptions();
-
+ FileAppender logOutput;
+
+ try {
+ logOutput = (FileAppender) rootAppenders.nextElement();
+ // assume there's only the one, and that it's a file appender
+ logOutput.setFile("log/broker" + counter + ".trace");
+ logOutput.activateOptions();
+ }
+ catch (Exception ignored) {
+ try {
+ PatternLayout layout = new PatternLayout("%r %-5p %c{2}: %m%n");
+ String fileName = "log/broker" + counter + ".trace";
+ logOutput = new FileAppender(layout, fileName);
+ logOutput.activateOptions();
+ root.addAppender(logOutput);
+
+ root.setLevel(Level.WARN);
+ }
+ catch (Exception ignored2) {
+ // TODO What?
+ }
+ }
+
Logger state = Logger.getLogger("State");
@SuppressWarnings("unchecked")
Enumeration stateAppenders = state.getAllAppenders();
- FileAppender stateOutput = (FileAppender) stateAppenders.nextElement();
- // assume there's only the one, and that it's a file appender
- stateOutput.setFile("log/broker" + counter + ".state");
- stateOutput.activateOptions();
+ FileAppender stateOutput;
+
+ try {
+ stateOutput = (FileAppender) stateAppenders.nextElement();
+ // assume there's only the one, and that it's a file appender
+ stateOutput.setFile("log/broker" + counter + ".state");
+ stateOutput.activateOptions();
+ }
+ catch (Exception ignored) {
+ try {
+ PatternLayout layout = new PatternLayout("%r::%m%n");
+ String fileName = "log/broker" + counter + ".state";
+ stateOutput = new FileAppender(layout, fileName);
+ stateOutput.activateOptions();
+ state.addAppender(stateOutput);
+
+ state.setLevel(Level.INFO);
+ }
+ catch (Exception ignored2) {
+ // TODO What?
+ }
+ }
}
}
diff --git a/src/main/java/org/powertac/samplebroker/core/ClassPathHacker.java b/src/main/java/org/powertac/samplebroker/core/ClassPathHacker.java
new file mode 100644
index 0000000..dcc9392
--- /dev/null
+++ b/src/main/java/org/powertac/samplebroker/core/ClassPathHacker.java
@@ -0,0 +1,73 @@
+package org.powertac.samplebroker.core;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+
+public class ClassPathHacker
+{
+ public static void loadJarDynamically ()
+ {
+ String jarPath = BrokerMain.class.getProtectionDomain()
+ .getCodeSource().getLocation().getFile();
+ URLClassLoader ucl = (URLClassLoader) ClassLoader.getSystemClassLoader();
+ boolean found = false;
+ for (URL url : ucl.getURLs()) {
+ if (url.getFile().equals(jarPath)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ ClassPathHacker.addFile(jarPath);
+ }
+ }
+
+ private static void addFile (final String s)
+ {
+ try {
+ addURL((new File(s)).toURI().toURL());
+ }
+ catch (IOException ignored) {
+ System.out.println();
+ System.out.println("Unable to load " + s + "dynamically, exiting!");
+ System.out.println();
+ System.exit(1);
+ }
+ }
+
+ private static void addURL (final URL u) throws IOException
+ {
+ final URLClassLoader urlClassLoader = getUrlClassLoader();
+
+ try {
+ final Method method = getAddUrlMethod();
+ method.setAccessible(true);
+ method.invoke(urlClassLoader, u);
+ }
+ catch (final Exception e) {
+ throw new IOException("Error, could not add URL to system classloader");
+ }
+ }
+
+ private static Method getAddUrlMethod () throws NoSuchMethodException
+ {
+ final Class urlclassloader = URLClassLoader.class;
+ return urlclassloader.getDeclaredMethod("addURL", new Class[]{URL.class});
+ }
+
+ private static URLClassLoader getUrlClassLoader ()
+ {
+ final ClassLoader sysloader = ClassLoader.getSystemClassLoader();
+ if (sysloader instanceof URLClassLoader) {
+ return (URLClassLoader) sysloader;
+ }
+ else {
+ throw new IllegalStateException("Not an UrlClassLoader: " + sysloader);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/matlab/portfoliomanager/CustomerRecord.m b/src/main/matlab/portfoliomanager/CustomerRecord.m
new file mode 100644
index 0000000..f2a4bae
--- /dev/null
+++ b/src/main/matlab/portfoliomanager/CustomerRecord.m
@@ -0,0 +1,109 @@
+classdef CustomerRecord < handle
+ % Keeps track of customer status and usage. Usage is stored
+ % per-customer-unit, but reported as the product of the per-customer
+ % quantity and the subscribed population. This allows the broker to use
+ % historical usage data as the subscribed population shifts.
+
+ properties
+ customer = 'None';
+ subscribedPopulation = 0;
+ usage = [];
+ alpha = 0.3;
+ end
+
+ methods
+ function obj = CustomerRecord (inp)
+ global pmManager
+
+ if strcmp(class(inp), 'org.powertac.common.CustomerInfo')
+ obj.customer = inp;
+ for i=1:pmManager.context.getUsageRecordLength()
+ obj.usage(i) = 0;
+ end
+ elseif strcmp(class(inp), 'CustomerRecord')
+ obj.customer = inp.customer;
+ obj.usage = repmat(inp.usage, 1);
+ elseif isnumeric(inp) && isempty(inp)
+ obj.customer = inp;
+ for i=1:pmManager.context.getUsageRecordLength()
+ obj.usage(i) = 0;
+ end
+ end
+
+ while length(obj.usage) < pmManager.context.getUsageRecordLength()
+ obj.usage(length(obj.usage) + 1) = 0;
+ end
+ end
+
+ % Adds new individuals to the count
+ function signup (obj, population)
+ obj.subscribedPopulation = min(obj.customer.getPopulation(), ...
+ obj.subscribedPopulation + population);
+ end
+
+ % Removes individuals from the count
+ function withdraw (obj, population)
+ obj.subscribedPopulation = obj.subscribedPopulation - population;
+ end
+
+ function produceConsume (obj, kwh, rawIndex)
+ % Customer produces or consumes power. We assume the kwh value is negative
+ % for production, positive for consumption
+ % store profile data at the given index
+
+ global pmManager
+
+ index = obj.getIndex(rawIndex);
+ if obj.subscribedPopulation == 0.0
+ kwhPerCustomer = 0;
+ else
+ kwhPerCustomer = kwh / obj.subscribedPopulation;
+ end
+ oldUsage = obj.usage(index);
+
+ if oldUsage == 0.0
+ % assume this is the first time
+ obj.usage(index) = kwhPerCustomer;
+ else
+ % exponential smoothing
+ obj.usage(index) = obj.alpha * kwhPerCustomer + (1.0 - obj.alpha) * oldUsage;
+ end
+
+ if isnumeric(obj.customer) && isempty(obj.customer)
+ name = 'null';
+ else
+ name = char(obj.customer.getName());
+ end
+
+ msg = horzcat('consume ', num2str(kwh), ' at ', int2str(index), ...
+ ', customer ', name);
+ pmManager.log.debug(msg);
+ end
+
+ function result = getUsage (obj, index)
+ global pmManager
+
+ if index < 1
+ msg = horzcat('usage requested for negative index ', int2str(index));
+ pmManager.log.warn(msg);
+ index = 1;
+ end
+
+ result = obj.usage(obj.getIndex(index)) * obj.subscribedPopulation;
+ end
+
+ function index = getIndex (obj, rawIndex)
+ global pmManager
+
+ if strcmp(class(rawIndex), 'double')
+ index = rawIndex;
+ elseif strcmp(class(rawIndex), 'org.joda.time.Instant')
+ diff = rawIndex.getMillis() - pmManager.timeService.getBase();
+ duration = org.powertac.common.Competition.currentCompetition().getTimeslotDuration();
+ index = floor(diff / duration) + 1;
+ end
+
+ index = mod(index-1, pmManager.context.getUsageRecordLength()) + 1;
+ end
+ end
+end
\ No newline at end of file
diff --git a/src/main/matlab/portfoliomanager/activate.m b/src/main/matlab/portfoliomanager/activate.m
new file mode 100644
index 0000000..a178f4e
--- /dev/null
+++ b/src/main/matlab/portfoliomanager/activate.m
@@ -0,0 +1,15 @@
+function activate (timeslotIndex)
+ % Called after TimeslotComplete msg received. Note that activation order
+ % among modules is non-deterministic.
+
+ global pmManager
+
+ if length(pmManager.customerSubscriptions) == 0
+ % we (most likely) have no tariffs
+ createInitialTariffs();
+ else
+ % we have some, are they good enough?
+ improveTariffs();
+ end
+
+end
diff --git a/src/main/matlab/portfoliomanager/collectUsage.m b/src/main/matlab/portfoliomanager/collectUsage.m
new file mode 100644
index 0000000..5c5708b
--- /dev/null
+++ b/src/main/matlab/portfoliomanager/collectUsage.m
@@ -0,0 +1,14 @@
+function result = collectUsage (index)
+ % Returns total usage for a given timeslot (represented as a simple index).
+
+ global pmManager
+
+ result = 0.0;
+ for k1 = keys(pmManager.customerSubscriptions)
+ customer_map = pmManager.customerSubscriptions(cell2mat(k1));
+ for k2 = keys(customer_map)
+ record = customer_map(cell2mat(k2));
+ result = result + record.getUsage(index);
+ end
+ end
+end
\ No newline at end of file
diff --git a/src/main/matlab/portfoliomanager/createInitialTariffs.m b/src/main/matlab/portfoliomanager/createInitialTariffs.m
new file mode 100644
index 0000000..4cc6e27
--- /dev/null
+++ b/src/main/matlab/portfoliomanager/createInitialTariffs.m
@@ -0,0 +1,44 @@
+function createInitialTariffs ()
+ % Creates initial tariffs for the main power types. These are simple
+ % fixed-rate two-part tariffs that give the broker a fixed margin.
+
+ global pmManager
+
+ % remember that market prices are per mwh, but tariffs are by kwh
+ marketPrice = pmManager.marketManager.getMeanMarketPrice() / 1000;
+
+ % for each power type representing a customer population,
+ % create a tariff that's better than what's available
+ for ptString = keys(pmManager.customerProfiles)
+ pt = org.powertac.common.enumerations.PowerType.valueOf(cell2mat(ptString));
+
+ % we'll just do fixed-rate tariffs for now
+ if pt.isConsumption()
+ rateValue = (marketPrice + pmManager.fixedPerKwh) * ...
+ (1.0 + pmManager.defaultMargin);
+ else
+ rateValue = -2.0 * marketPrice;
+ end
+
+ if pt.isInterruptible()
+ % Magic number!! price break for interruptible
+ rateValue = rateValue * 0.7;
+ end
+
+ broker = pmManager.context.getBroker();
+ spec = org.powertac.common.TariffSpecification(broker, pt). ...
+ withPeriodicPayment(pmManager.defaultPeriodicPayment);
+
+ rate = org.powertac.common.Rate().withValue(rateValue);
+
+ if pt.isInterruptible()
+ % set max curtailment
+ rate.withMaxCurtailment(0.1);
+ end
+
+ spec.addRate(rate);
+ pmManager.customerSubscriptions(char(spec)) = containers.Map;
+ pmManager.tariffRepo.addSpecification(spec);
+ pmManager.context.sendMessage(spec);
+ end
+end
\ No newline at end of file
diff --git a/src/main/matlab/portfoliomanager/getCustomerRecordByPowerType.m b/src/main/matlab/portfoliomanager/getCustomerRecordByPowerType.m
new file mode 100644
index 0000000..0c96ddf
--- /dev/null
+++ b/src/main/matlab/portfoliomanager/getCustomerRecordByPowerType.m
@@ -0,0 +1,26 @@
+function record = getCustomerRecordByPowerType (powerType, customer)
+ global pmManager
+
+ typeString = char(powerType);
+ % customerName = char(customer.getName());
+ if isnumeric(customer) && isempty(customer)
+ % customer might be null (Java) == [] (MatLab)
+ customerName = 'null';
+ else
+ customerName = char(customer.getName());
+ end
+
+ if ~pmManager.customerProfiles.isKey(typeString)
+ customerMap = containers.Map;
+ pmManager.customerProfiles(typeString) = customerMap;
+ else
+ customerMap = pmManager.customerProfiles(typeString);
+ end
+
+ if ~customerMap.isKey(customerName)
+ record = CustomerRecord(customer);
+ customerMap(customerName) = record;
+ else
+ record = customerMap(customerName);
+ end
+end
diff --git a/src/main/matlab/portfoliomanager/getCustomerRecordByTariff.m b/src/main/matlab/portfoliomanager/getCustomerRecordByTariff.m
new file mode 100644
index 0000000..75de397
--- /dev/null
+++ b/src/main/matlab/portfoliomanager/getCustomerRecordByTariff.m
@@ -0,0 +1,27 @@
+function record = getCustomerRecordByTariff (spec, customer)
+ global pmManager
+
+ specString = char(spec);
+ if isnumeric(customer) && isempty(customer)
+ % customer might be null (Java) == [] (MatLab)
+ customerName = 'null';
+ else
+ customerName = char(customer.getName());
+ end
+
+ if ~pmManager.customerSubscriptions.isKey(specString)
+ customerMap = containers.Map;
+ pmManager.customerSubscriptions(specString) = customerMap;
+ else
+ customerMap = pmManager.customerSubscriptions(specString);
+ end
+
+ if ~customerMap.isKey(customerName)
+ % seed with the generic record for this customer
+ oldRecord = getCustomerRecordByPowerType(spec.getPowerType(), customer);
+ record = CustomerRecord(oldRecord);
+ customerMap(customerName) = record;
+ else
+ record = customerMap(customerName);
+ end
+end
\ No newline at end of file
diff --git a/src/main/matlab/portfoliomanager/improveTariffs.m b/src/main/matlab/portfoliomanager/improveTariffs.m
new file mode 100644
index 0000000..714fee2
--- /dev/null
+++ b/src/main/matlab/portfoliomanager/improveTariffs.m
@@ -0,0 +1,93 @@
+function improveTariffs ()
+ % Checks to see whether our tariffs need fine-tuning
+
+ global pmManager
+
+ timeslotIndex = pmManager.timeslotRepo.currentTimeslot().getSerialNumber();
+
+ % quick magic-number hack to inject a balancing order
+ if timeslotIndex == 371
+ broker = pmManager.context.getBroker();
+ test = org.powertac.common.enumerations.PowerType.INTERRUPTIBLE_CONSUMPTION;
+ specs = pmManager.tariffRepo.findTariffSpecificationsByBroker(broker).toArray();
+ for i = 1:length(specs)
+ spec = specs(i);
+ if spec.getPowerType() == test
+ order = org.powertac.common.msg.BalancingOrder(...
+ broker, spec, 0.5, spec.getRates().get(0).getMinValue() * 0.9);
+ pmManager.context.sendMessage(order);
+ end
+ end
+ end
+
+ % magic-number hack to supersede a tariff
+ if timeslotIndex == 380
+ broker = pmManager.context.getBroker();
+ test = org.powertac.common.enumerations.PowerType.CONSUMPTION;
+
+ % find the existing CONSUMPTION tariff
+ oldc = [];
+ candidates = pmManager.tariffRepo.findTariffSpecificationsByBroker(broker).toArray();
+ if isnumeric(candidates) || isempty(candidates) % Test for null
+ pmManager.log.error('No tariffs found for broker');
+ else
+ for i = 1:length(candidates)
+ candidate = candidates(i);
+ if candidate.getPowerType() == test
+ oldc = candidate;
+ break;
+ end
+ end
+
+ if oldc == []
+ pmManager.log.warn('No CONSUMPTION tariffs found');
+ else
+ rateValue = oldc.getRates().get(0).getValue();
+ % create a new CONSUMPTION tariff
+ spec = org.powertac.common.TariffSpecification(broker, test). ...
+ withPeriodicPayment(pmManager.defaultPeriodicPayment * 1.1);
+ rate = org.powertac.common.Rate().withValue(rateValue);
+ spec.addRate(rate);
+ spec.addSupersedes(oldc.getId());
+
+ pmManager.tariffRepo.addSpecification(spec);
+ pmManager.context.sendMessage(spec);
+ % revoke the old one
+ revoke = org.powertac.common.msg.TariffRevoke(broker, oldc);
+ pmManager.context.sendMessage(revoke);
+ end
+ end
+ end
+
+% % magic-number hack to supersede a tariff
+% if timeslotIndex == 380
+% broker = pmManager.context.getBroker();
+% type = org.powertac.common.enumerations.PowerType.CONSUMPTION;
+%
+% % find the existing CONSUMPTION tariff
+% oldc = [];
+% candidates = pmManager.tariffRepo.findTariffSpecificationsByPowerType(type);
+% if isnumeric(candidates) || isempty(candidates) % Test for null
+% pmManager.log.error('No CONSUMPTION tariffs found');
+% end
+% candidates = candidates.toArray();
+% oldc = candidates(1);
+%
+% rateValue = oldc.getRates().get(0).getValue();
+%
+% % create a new CONSUMPTION tariff
+% spec = org.powertac.common.TariffSpecification(broker, type). ...
+% withPeriodicPayment(pmManager.defaultPeriodicPayment * 1.1);
+% rate = org.powertac.common.Rate().withValue(rateValue);
+%
+% if ~isnumeric(oldc) && ~isempty(oldc)
+% spec.addSupersedes(oldc.getId());
+% end
+%
+% pmManager.tariffRepo.addSpecification(spec);
+% pmManager.context.sendMessage(spec);
+% % revoke the old one
+% revoke = org.powertac.common.msg.TariffRevoke(broker, oldc);
+% pmManager.context.sendMessage(revoke);
+% end
+end
diff --git a/src/main/matlab/portfoliomanager/mInit.m b/src/main/matlab/portfoliomanager/mInit.m
new file mode 100644
index 0000000..3de6069
--- /dev/null
+++ b/src/main/matlab/portfoliomanager/mInit.m
@@ -0,0 +1,25 @@
+function mInit (var0, var1, var2, var3, var4, var5, var6, var7)
+ global pmManager
+
+ pmManager.context = var0;
+ pmManager.timeslotRepo = var1;
+ pmManager.tariffRepo = var2;
+ pmManager.customerRepo = var3;
+ pmManager.marketManager = var4;
+ pmManager.timeService = var5;
+ pmManager.log = var6;
+ pmManager.null = var7;
+
+ % Configurable parameters for tariff composition
+ pmManager.defaultMargin = 0.5;
+ pmManager.fixedPerKwh = -0.06;
+ pmManager.defaultPeriodicPayment = -1.0;
+
+ % ---- Portfolio records -----
+ % Customer records indexed by power type and by tariff. Note that the
+ % CustomerRecord instances are NOT shared between these structures, because
+ % we need to keep track of subscriptions by tariff.
+ pmManager.customerProfiles = containers.Map;
+ pmManager.customerSubscriptions = containers.Map;
+ pmManager.competingTariffs = containers.Map;
+end
\ No newline at end of file
diff --git a/src/main/matlab/portfoliomanager/msgBalancingControlEvent.m b/src/main/matlab/portfoliomanager/msgBalancingControlEvent.m
new file mode 100644
index 0000000..763cca3
--- /dev/null
+++ b/src/main/matlab/portfoliomanager/msgBalancingControlEvent.m
@@ -0,0 +1,10 @@
+function msgBalancingControlEvent (bce)
+ % Handles a BalancingControlEvent, sent when a BalancingOrder is
+ % exercised by the DU.
+
+ global pmManager
+
+ kwh = bce.getKwh();
+ msg = horzcat('BalancingControlEvent ', num2str(kwh));
+ pmManager.log.info(msg);
+end
\ No newline at end of file
diff --git a/src/main/matlab/portfoliomanager/msgCustomerBootstrapData.m b/src/main/matlab/portfoliomanager/msgCustomerBootstrapData.m
new file mode 100644
index 0000000..e1162bf
--- /dev/null
+++ b/src/main/matlab/portfoliomanager/msgCustomerBootstrapData.m
@@ -0,0 +1,23 @@
+function msgCustomerBootstrapData (cbd)
+ % Handles CustomerBootstrapData by populating the customer model
+ % corresponding to the given customer and power type. This gives the
+ % broker a running start.
+
+ global pmManager
+
+ name = cbd.getCustomerName();
+ type = cbd.getPowerType();
+ usage = cbd.getNetUsage();
+
+ customer = pmManager.customerRepo.findByNameAndPowerType(name, type);
+ record = getCustomerRecordByPowerType(cbd.getPowerType(), customer);
+
+ check1 = record.subscribedPopulation;
+
+ subs = record.subscribedPopulation;
+ record.subscribedPopulation = customer.getPopulation();
+ for i=1:length(usage)
+ record.produceConsume(usage(i), i);
+ end
+ record.subscribedPopulation = subs;
+end
\ No newline at end of file
diff --git a/src/main/matlab/portfoliomanager/msgTariffRevoke.m b/src/main/matlab/portfoliomanager/msgTariffRevoke.m
new file mode 100644
index 0000000..0fbcd7e
--- /dev/null
+++ b/src/main/matlab/portfoliomanager/msgTariffRevoke.m
@@ -0,0 +1,37 @@
+function msgTariffRevoke (tr)
+ % Handles a TariffRevoke message from the server, indicating that some
+ % tariff has been revoked.
+
+ global pmManager
+
+ source = tr.getBroker();
+ name = char(source.getUsername());
+ contextName = char(pmManager.context.getBrokerUsername());
+
+ % if it's from some other broker, we need to remove it from the
+ % tariffRepo, and from the competingTariffs list
+ if ~strcmp(name, contextName)
+ pmManager.log.info('clear out competing tariff');
+
+ original = pmManager.tariffRepo.findSpecificationById(tr.getTariffId());
+
+ if isnumeric(original) && isempty(original)
+ msg = horzcat('Original tariff ', int2str(tr.getTariffId()), 'not found');
+ pmManager.log.warn(msg);
+ return;
+ end
+
+ pmManager.tariffRepo.removeSpecification(original.getId());
+
+ typeString = char(original.getPowerType());
+ if ~pmManager.competingTariffs.isKey(typeString)
+ pmManager.log.warn('Candidate list is null');
+ return;
+ else
+ % candidates is an array with tariff specs, remove original
+ candidates = pmManager.competingTariffs(typeString);
+ candidates = candidates(candidates~=original);
+ pmManager.competingTariffs(typeString) = candidates;
+ end
+ end
+end
\ No newline at end of file
diff --git a/src/main/matlab/portfoliomanager/msgTariffSpecification.m b/src/main/matlab/portfoliomanager/msgTariffSpecification.m
new file mode 100644
index 0000000..87c752a
--- /dev/null
+++ b/src/main/matlab/portfoliomanager/msgTariffSpecification.m
@@ -0,0 +1,40 @@
+function msgTariffSpecification (spec)
+ % Handles a TariffSpecification. These are sent by the server when new tariffs
+ % are published. If it's not ours, then it's a competitor's tariff. We keep
+ % track of competing tariffs locally, and we also store them in the tariffRepo.
+
+ global pmManager
+
+ theBroker = spec.getBroker();
+ theBrokerName = char(theBroker.getUsername());
+ contextBrokerName = char(pmManager.context.getBrokerUsername());
+
+ if strcmp(theBrokerName, contextBrokerName)
+ % if it's ours, just log it, because we already put it in the repo
+ original = pmManager.tariffRepo.findSpecificationById(spec.getId());
+ if ~isa(original, 'org.powertac.common.TariffSpecification')
+ pmManager.log.error(horzcat('Spec ', int2str(spec.getId()), ' not in local repo'));
+ end
+ pmManager.log.info(horzcat('published ', char(spec.toString())));
+ else
+ % otherwise, keep track of competing tariffs, and record in the repo
+ addCompetingTariff(spec);
+ pmManager.tariffRepo.addSpecification(spec);
+ end
+end
+
+function addCompetingTariff (spec)
+ % Finds the list of competing tariffs for the given PowerType, add tariff
+ global pmManager
+
+ typeString = char(spec.getPowerType());
+
+ if ~pmManager.competingTariffs.isKey(typeString)
+ tariffs = [];
+ else
+ tariffs = pmManager.competingTariffs(typeString);
+ end
+
+ tariffs = [tariffs spec];
+ pmManager.competingTariffs(typeString) = tariffs;
+end
\ No newline at end of file
diff --git a/src/main/matlab/portfoliomanager/msgTariffStatus.m b/src/main/matlab/portfoliomanager/msgTariffStatus.m
new file mode 100644
index 0000000..e019e92
--- /dev/null
+++ b/src/main/matlab/portfoliomanager/msgTariffStatus.m
@@ -0,0 +1,10 @@
+function msgTariffStatus (ts)
+ % Handles a TariffStatus message. This should do something when the status
+ % is not SUCCESS.
+
+ global pmManager
+
+ status = ts.getStatus();
+ msg = horzcat('TariffStatus: ', char(status));
+ pmManager.log.info(msg);
+end
\ No newline at end of file
diff --git a/src/main/matlab/portfoliomanager/msgTariffTransaction.m b/src/main/matlab/portfoliomanager/msgTariffTransaction.m
new file mode 100644
index 0000000..7273e07
--- /dev/null
+++ b/src/main/matlab/portfoliomanager/msgTariffTransaction.m
@@ -0,0 +1,55 @@
+function msgTariffTransaction (ttx)
+ % Handles a TariffTransaction. We only care about certain types:
+ % PRODUCE, CONSUME, SIGNUP, and WITHDRAW.
+
+ global pmManager
+
+ txTypeString = char(ttx.getTxType());
+ newSpec = ttx.getTariffSpec();
+
+ % make sure we have this tariff
+ if ~isa(newSpec, 'org.powertac.common.TariffSpecification')
+ msg = horzcat('TariffTransaction type=', txTypeString, ' for unknown spec');
+ pmManager.log.error(msg);
+ else
+ oldSpec = pmManager.tariffRepo.findSpecificationById(newSpec.getId());
+ if oldSpec.getId() ~= newSpec.getId()
+ msg = horzcat('Incoming spec ', num2str(newSpec.getId()), ...
+ ' not matched in repo');
+ pmManager.log.error(msg);
+ end
+ end
+
+ % ttx.getCustomerInfo() might return null => [] in MatLab
+ record = getCustomerRecordByTariff(newSpec, ttx.getCustomerInfo());
+
+ enumType = 'org.powertac.common.TariffTransaction$Type';
+ SIGNUP = javaMethod('valueOf', enumType, 'SIGNUP');
+ WITHDRAW = javaMethod('valueOf', enumType, 'WITHDRAW');
+ PRODUCE = javaMethod('valueOf', enumType, 'PRODUCE');
+ PUBLISH = javaMethod('valueOf', enumType, 'PUBLISH');
+
+ if strcmp(txTypeString, char(SIGNUP))
+ % keep track of customer counts
+ record.signup(ttx.getCustomerCount());
+ elseif strcmp(txTypeString, char(WITHDRAW))
+ % customers presumably found a better deal
+ record.withdraw(ttx.getCustomerCount());
+ elseif strcmp(txTypeString, char(PRODUCE))
+ % if ttx count and subscribe population don't match, it will be hard
+ % to estimate per-individual production
+ if ttx.getCustomerCount() ~= record.subscribedPopulation
+ msg = horzcat('production by subset ', num2str(ttx.getCustomerCount()),...
+ ' of subscribed population ', int2str(record.subscribedPopulation));
+ pmManager.log.warn(msg);
+ end
+ record.produceConsume(ttx.getKWh(), ttx.getPostedTime());
+ elseif strcmp(txTypeString, char(PUBLISH))
+ if ttx.getCustomerCount() ~= record.subscribedPopulation
+ msg = horzcat('production by subset ', num2str(ttx.getCustomerCount()),...
+ ' of subscribed population ', int2str(record.subscribedPopulation));
+ pmManager.log.warn(msg);
+ end
+ record.produceConsume(ttx.getKWh(), ttx.getPostedTime());
+ end
+end
diff --git a/src/main/matlab/portfoliomanager/testFunction.m b/src/main/matlab/portfoliomanager/testFunction.m
new file mode 100644
index 0000000..d618b5d
--- /dev/null
+++ b/src/main/matlab/portfoliomanager/testFunction.m
@@ -0,0 +1,3 @@
+function result = testFunction (var0)
+ result = 10 * var0;
+end
diff --git a/src/test/java/org/powertac/samplebroker/PortfolioManagerTest.java b/src/test/java/org/powertac/samplebroker/PortfolioManagerTest.java
deleted file mode 100644
index 7f6ae7e..0000000
--- a/src/test/java/org/powertac/samplebroker/PortfolioManagerTest.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (c) 2012 by the original author
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.powertac.samplebroker;
-
-import static org.junit.Assert.*;
-import static org.mockito.Mockito.*;
-
-import java.util.Arrays;
-
-import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
-import org.joda.time.Instant;
-import org.junit.Before;
-import org.junit.Test;
-import org.powertac.common.CustomerInfo;
-import org.powertac.common.TimeService;
-import org.powertac.common.Timeslot;
-import org.powertac.common.enumerations.PowerType;
-import org.powertac.common.msg.CustomerBootstrapData;
-import org.powertac.common.repo.CustomerRepo;
-import org.powertac.common.repo.TimeslotRepo;
-import org.powertac.samplebroker.core.BrokerPropertiesService;
-import org.powertac.samplebroker.core.PowerTacBroker;
-import org.springframework.test.util.ReflectionTestUtils;
-
-/**
- * @author jcollins
- */
-public class PortfolioManagerTest
-{
- private TimeslotRepo timeslotRepo;
- private CustomerRepo customerRepo;
-
- private PortfolioManagerService portfolioManagerService;
- private PowerTacBroker broker;
- private Instant baseTime;
-
- /**
- *
- */
- @Before
- public void setUp () throws Exception
- {
- broker = mock(PowerTacBroker.class);
- timeslotRepo = mock(TimeslotRepo.class);
- customerRepo = new CustomerRepo();
- BrokerPropertiesService bps = mock(BrokerPropertiesService.class);
- when(broker.getUsageRecordLength()).thenReturn(7*24);
- portfolioManagerService = new PortfolioManagerService();
- ReflectionTestUtils.setField(portfolioManagerService,
- "timeslotRepo",
- timeslotRepo);
- ReflectionTestUtils.setField(portfolioManagerService,
- "customerRepo",
- customerRepo);
- ReflectionTestUtils.setField(portfolioManagerService,
- "propertiesService",
- bps);
- portfolioManagerService.initialize(broker);
-
- // set the time
- baseTime =
- new DateTime(2011, 2, 1, 0, 0, 0, 0, DateTimeZone.UTC).toInstant();
- }
-
- /**
- * Test customer boot data
- */
- @Test
- public void testCustomerBootstrap ()
- {
- // set up a competition
- CustomerInfo podunk = new CustomerInfo("Podunk", 3);
- customerRepo.add(podunk);
- CustomerInfo midvale = new CustomerInfo("Midvale", 1000);
- customerRepo.add(midvale);
- // create a Timeslot for use by the bootstrap data
- Timeslot ts0 = new Timeslot(8*24, baseTime.plus(TimeService.DAY * 8));
- when(timeslotRepo.currentTimeslot()).thenReturn(ts0);
- // send to broker and check
- double[] podunkData = new double[7*24];
- Arrays.fill(podunkData, 3.6);
- double[] midvaleData = new double[7*24];
- Arrays.fill(midvaleData, 1600.0);
- CustomerBootstrapData boot =
- new CustomerBootstrapData(podunk, PowerType.CONSUMPTION, podunkData);
- portfolioManagerService.handleMessage(boot);
- boot = new CustomerBootstrapData(midvale, PowerType.CONSUMPTION, midvaleData);
- portfolioManagerService.handleMessage(boot);
- double[] podunkUsage =
- portfolioManagerService.getRawUsageForCustomer(podunk).get(PowerType.CONSUMPTION);
- assertNotNull("podunk usage is recorded", podunkUsage);
- assertEquals("correct usage value for podunk", 1.2, podunkUsage[23], 1e-6);
- double[] midvaleUsage =
- portfolioManagerService.getRawUsageForCustomer(midvale).get(PowerType.CONSUMPTION);
- assertNotNull("midvale usage is recorded", midvaleUsage);
- assertEquals("correct usage value for midvale", 1.6, midvaleUsage[27], 1e-6);
- }
-
- // other tests needed...
-}
diff --git a/test.m b/test.m
new file mode 100644
index 0000000..655b93e
--- /dev/null
+++ b/test.m
@@ -0,0 +1,6 @@
+% Run this with : matlab -nodisplay -nosplash -nodesktop -r test
+
+javaaddpath('target/sample-broker.jar');
+
+o = org.powertac.samplebroker.core.BrokerMain;
+javaMethod('mainMatlab', o, '--jms-url=tcp://130.115.197.116:61616');