/* ==================================================================
* MockGenerationLimitControl.java - Mar 25, 2014 10:49:57 AM
*
* Copyright 2007-2014 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.control.demandbalancer.mock;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import net.solarnetwork.domain.NodeControlInfo;
import net.solarnetwork.domain.NodeControlPropertyType;
import net.solarnetwork.node.DatumDataSource;
import net.solarnetwork.node.NodeControlProvider;
import net.solarnetwork.node.domain.Datum;
import net.solarnetwork.node.domain.NodeControlInfoDatum;
import net.solarnetwork.node.reactor.Instruction;
import net.solarnetwork.node.reactor.InstructionHandler;
import net.solarnetwork.node.reactor.InstructionStatus.InstructionState;
import net.solarnetwork.node.util.ClassUtils;
import net.solarnetwork.util.OptionalService;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventAdmin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Mock implementation of {@link NodeControlProvider} that acts like a
* generation limiting device, to support testing of demand balancing. This
* control supports an <em>integer percentage</em> value that represents a
* generation output limit, e.g. {@code 0} means no generation output is allowed
* while {@code 100} means full output is allowed.
*
* Both the {@link InstructionHandler#TOPIC_DEMAND_BALANCE} and
* {@link InstructionHandler#TOPIC_SET_CONTROL_PARAMETER} topics are supported,
* both of which expect a {@code controlId} key associated with an integer
* percentage value.
*
* <p>
* The configurable properties of this class are:
* </p>
*
* <dl class="class-properties">
* <dt>controlId</dt>
* <dd>The control ID to use. Defaults to {@code /power/limit/mock}.</dd>
* </dl>
*
* @author matt
* @version 1.1
*/
public class MockGenerationLimitControl implements NodeControlProvider, InstructionHandler {
private final AtomicInteger limit = new AtomicInteger(100);
private OptionalService<EventAdmin> eventAdmin;
private String controlId = "/power/limit/mock";
private final Logger log = LoggerFactory.getLogger(getClass());
@Override
public String getUID() {
return controlId;
}
@Override
public String getGroupUID() {
return "Mock";
}
@Override
public List<String> getAvailableControlIds() {
return Collections.singletonList(controlId);
}
@Override
public NodeControlInfo getCurrentControlInfo(String controlId) {
NodeControlInfoDatum info = new NodeControlInfoDatum();
info.setCreated(new Date());
info.setSourceId(controlId);
info.setType(NodeControlPropertyType.Integer);
info.setReadonly(false);
info.setValue(limit.toString());
postControlCapturedEvent(info);
return info;
}
private InstructionState setLimitValue(final int value) {
if ( value < 0 || value > 100 ) {
return InstructionState.Declined;
}
limit.set(value);
return InstructionState.Completed;
}
// InstructionHandler
@Override
public boolean handlesTopic(String topic) {
return (InstructionHandler.TOPIC_SET_CONTROL_PARAMETER.equals(topic) || InstructionHandler.TOPIC_DEMAND_BALANCE
.equals(topic));
}
@Override
public InstructionState processInstruction(Instruction instruction) {
// look for a parameter name that matches a control ID
InstructionState result = null;
log.debug("Inspecting instruction {} against control {}", instruction.getId(), controlId);
for ( String paramName : instruction.getParameterNames() ) {
log.trace("Got instruction parameter {}", paramName);
if ( controlId.equals(paramName) ) {
String str = instruction.getParameterValue(controlId);
Integer desiredValue = Integer.parseInt(str);
log.info("Percent output limit request of {}%", desiredValue);
result = setLimitValue(desiredValue);
break;
}
}
return result;
}
/**
* Post a {@link NodeControlProvider#EVENT_TOPIC_CONTROL_INFO_CAPTURED}
* {@link Event}.
*
* <p>
* This method calls {@link #createControlCapturedEvent(NodeControlInfo)} to
* create the actual Event, which may be overridden by extending classes.
* </p>
*
* @param info
* the {@link NodeControlInfo} to post the event for
* @since 1.1
*/
protected final void postControlCapturedEvent(final NodeControlInfo info) {
EventAdmin ea = (eventAdmin == null ? null : eventAdmin.service());
if ( ea == null || info == null ) {
return;
}
Event event = createControlCapturedEvent(info);
ea.postEvent(event);
}
/**
* Create a new
* {@link NodeControlProvider#EVENT_TOPIC_CONTROL_INFO_CAPTURED}
* {@link Event} object out of a {@link Datum}.
*
* <p>
* This method will populate all simple properties of the given
* {@link Datum} into the event properties, along with the
* {@link DatumDataSource#EVENT_DATUM_CAPTURED_DATUM_TYPE}.
*
* @param info
* the info to create the event for
* @return the new Event instance
* @since 1.1
*/
protected Event createControlCapturedEvent(final NodeControlInfo info) {
Map<String, Object> props = ClassUtils.getSimpleBeanProperties(info, null);
log.debug("Created {} event with props {}",
NodeControlProvider.EVENT_TOPIC_CONTROL_INFO_CAPTURED, props);
return new Event(NodeControlProvider.EVENT_TOPIC_CONTROL_INFO_CAPTURED, props);
}
// Accessors
public String getControlId() {
return controlId;
}
public void setControlId(String controlId) {
this.controlId = controlId;
}
public OptionalService<EventAdmin> getEventAdmin() {
return eventAdmin;
}
public void setEventAdmin(OptionalService<EventAdmin> eventAdmin) {
this.eventAdmin = eventAdmin;
}
}