/*
* Dog - Device Driver
*
* Copyright (c) 2012-2014 Dario Bonino and Luigi De Russis
*
* 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 it.polito.elite.dog.drivers.echelon.ilon100.singlephasenergymeter;
import it.polito.elite.dog.core.library.model.ControllableDevice;
import it.polito.elite.dog.core.library.model.DeviceStatus;
import it.polito.elite.dog.core.library.model.devicecategory.SinglePhaseEnergyMeter;
import it.polito.elite.dog.core.library.model.notification.SinglePhaseActiveEnergyMeasurementNotification;
import it.polito.elite.dog.core.library.model.notification.SinglePhaseReactiveEnergyMeasurementNotification;
import it.polito.elite.dog.core.library.model.state.SinglePhaseActiveEnergyState;
import it.polito.elite.dog.core.library.model.state.SinglePhaseReactiveEnergyState;
import it.polito.elite.dog.core.library.model.statevalue.ActiveEnergyStateValue;
import it.polito.elite.dog.core.library.model.statevalue.ReactiveEnergyStateValue;
import it.polito.elite.dog.core.library.util.LogHelper;
import it.polito.elite.dog.drivers.echelon.ilon100.network.EchelonIlon100DriverInstance;
import it.polito.elite.dog.drivers.echelon.ilon100.network.info.CmdNotificationInfo;
import it.polito.elite.dog.drivers.echelon.ilon100.network.info.DataPointInfo;
import it.polito.elite.dog.drivers.echelon.ilon100.network.interfaces.EchelonIlon100Network;
import java.lang.reflect.Method;
import java.util.Set;
import javax.measure.DecimalMeasure;
import javax.measure.Measure;
import javax.measure.quantity.Power;
import javax.measure.unit.NonSI;
import javax.measure.unit.SI;
import javax.measure.unit.Unit;
import javax.measure.unit.UnitFormat;
import org.osgi.framework.BundleContext;
import org.osgi.service.log.LogService;
/**
* A class implementing the driver for SinglePhaseEnergyMeters based on the
* Echelon iLon 100 web-service based network protocol
*
* @author <a href="mailto:dario.bonino@polito.it">Dario Bonino</a>
* @see <a href="http://elite.polito.it">http://elite.polito.it</a>
*
*/
public class EchelonIlon100SinglePhaseEnergyMeterDriverInstance extends EchelonIlon100DriverInstance implements
SinglePhaseEnergyMeter
{
// the class logger
private LogHelper logger;
/**
* The class constructor, initializes all the data structures needed for the
* driver to successfully handle SinglePhaseEnergyMeter devices
*
* @param network
* The network driver supporting communication towards the
* real-world iLon100 gateway
* @param device
* The device managed by this meter driver
* @param endpointAddress
* The endpoint address of the real iLon 100 gateway handling
* this device
* @param context
* The bundle context needed to instantiate the driver logger
*/
public EchelonIlon100SinglePhaseEnergyMeterDriverInstance(EchelonIlon100Network network, ControllableDevice device,
String endpointAddress, BundleContext context)
{
super(network, device, endpointAddress);
// create a logger
this.logger = new LogHelper(context);
// TODO: get the initial state of the device....(states can be updated
// by reading notification group addresses)
this.initializeStates();
}
@Override
public Measure<?, ?> getReactiveEnergyValue()
{
// direct read on the network...
// search the power notification...
/*
* for (DataPointInfo dp : this.datapoint2Notification.keySet()) { if
* (this.datapoint2Notification.get(dp).contains(
* SinglePhaseReactiveEnergyMeasurementNotification.notificationName)) {
* this.network.readDP(dp); return dp.getValueWithUnitOfMeasure(); } }
*/
return (Measure<?, ?>) this.currentState.getState(SinglePhaseReactiveEnergyState.class.getSimpleName())
.getCurrentStateValue()[0].getValue();
}
@Override
public Measure<?, ?> getActiveEnergyValue()
{
// direct read on the network...
// search the power notification...
/*
* for (DataPointInfo dp : this.datapoint2Notification.keySet()) { if
* (this.datapoint2Notification.get(dp).contains(
* SinglePhaseActiveEnergyMeasurementNotification.notificationName)) {
* this.network.readDP(dp); return dp.getValueWithUnitOfMeasure(); } }
* return null;
*/
return (Measure<?, ?>) this.currentState.getState(SinglePhaseActiveEnergyState.class.getSimpleName())
.getCurrentStateValue()[0].getValue();
}
@Override
public DeviceStatus getState()
{
return this.currentState;
}
@Override
public void notifyNewReactiveEnergyValue(Measure<?, ?> value)
{
// update the state
ReactiveEnergyStateValue rValue = new ReactiveEnergyStateValue();
rValue.setValue(value);
this.currentState.setState(SinglePhaseReactiveEnergyState.class.getSimpleName(),
new SinglePhaseReactiveEnergyState(rValue));
// forward the notification to the framework
((SinglePhaseEnergyMeter) this.device).notifyNewReactiveEnergyValue(value);
// log
this.logger.log(LogService.LOG_DEBUG, EchelonIlon100SinglePhaseEnergyMeterDriver.logId
+ "Dispatched new reactive energy notification value for " + this.device.getDeviceId() + ": " + value);
}
@Override
public void notifyNewActiveEnergyValue(Measure<?, ?> value)
{
// update the state
ActiveEnergyStateValue aValue = new ActiveEnergyStateValue();
aValue.setValue(value);
this.currentState.setState(SinglePhaseActiveEnergyState.class.getSimpleName(),
new SinglePhaseActiveEnergyState(aValue));
// forward the notification to the framework
((SinglePhaseEnergyMeter) this.device).notifyNewActiveEnergyValue(value);
// log
this.logger.log(LogService.LOG_DEBUG, EchelonIlon100SinglePhaseEnergyMeterDriver.logId
+ "Dispatched new active energy notification value for " + this.device.getDeviceId() + ": " + value);
}
@Override
public void newMessageFromHouse(DataPointInfo dataPointInfo)
{
// check value
if ((dataPointInfo.getValue() != Double.NaN) && (dataPointInfo.getValue() != Double.NEGATIVE_INFINITY)
&& (dataPointInfo.getValue() != Double.POSITIVE_INFINITY))
{
// gets the corresponding notification set...
Set<CmdNotificationInfo> notificationInfos = this.datapoint2Notification.get(dataPointInfo);
// handle the notifications
for (CmdNotificationInfo notificationInfo : notificationInfos)
{
// black magic here...
String notificationName = notificationInfo.getName();
// black magic here...
// get the hypothetical class method name
String notifyMethod = "notify" + Character.toUpperCase(notificationName.charAt(0))
+ notificationName.substring(1);
// search the method and execute it
try
{
// get the method
Method notify = EchelonIlon100SinglePhaseEnergyMeterDriverInstance.class.getDeclaredMethod(
notifyMethod, Measure.class);
// invoke the method
notify.invoke(this,
DecimalMeasure.valueOf(dataPointInfo.getValue() + " " + dataPointInfo.getUnitOfMeasure()));
}
catch (Exception e)
{
// log the error
this.logger.log(LogService.LOG_WARNING, EchelonIlon100SinglePhaseEnergyMeterDriver.logId
+ "Unable to find a suitable notification method for the datapoint: " + dataPointInfo
+ ":\n" + e);
}
// notify the monitor admin
this.updateStatus();
}
}
}
@Override
protected void specificConfiguration()
{
// prepare the device state map
this.currentState = new DeviceStatus(device.getDeviceId());
}
/**
* Initializes the inner states of the handled device...
*/
private void initializeStates()
{
// Since this driver handles the device metering according to a well
// defined interface, we can get the unit of measure from all the
// notifications handled by this device except from state notifications
// and fall back to WattHour/Var if the
// procedure fails...
// create the var and va units
Unit<Power> VAR = SI.WATT.alternate("var");
// add unit of measure aliases (to fix notation problems...)
UnitFormat uf = UnitFormat.getInstance();
uf.alias(SI.WATT.times(NonSI.HOUR), "Wh");
uf.label(SI.KILO(SI.WATT.times(NonSI.HOUR)), "kWh");
uf.alias(VAR.times(NonSI.HOUR), "Varh");
uf.label(SI.KILO(VAR.times(NonSI.HOUR)), "kVarh");
String activeEnergyUOM = SI.WATT.times(NonSI.HOUR).toString();
String reactiveEnergyUOM = VAR.times(NonSI.HOUR).toString();
// search the energy unit of measures declared in the device
// configuration
for (DataPointInfo dp : this.datapoint2Notification.keySet())
{
if (this.datapoint2Notification.get(dp).contains(
SinglePhaseActiveEnergyMeasurementNotification.notificationName))
{
activeEnergyUOM = dp.getUnitOfMeasure();
}
else if (this.datapoint2Notification.get(dp).contains(
SinglePhaseReactiveEnergyMeasurementNotification.notificationName))
{
reactiveEnergyUOM = dp.getUnitOfMeasure();
}
}
// create all the states
ActiveEnergyStateValue aValue = new ActiveEnergyStateValue();
aValue.setValue(DecimalMeasure.valueOf("0 " + activeEnergyUOM));
this.currentState.setState(SinglePhaseActiveEnergyState.class.getSimpleName(),
new SinglePhaseActiveEnergyState(aValue));
ReactiveEnergyStateValue rValue = new ReactiveEnergyStateValue();
rValue.setValue(DecimalMeasure.valueOf("0 " + reactiveEnergyUOM));
this.currentState.setState(SinglePhaseReactiveEnergyState.class.getSimpleName(),
new SinglePhaseReactiveEnergyState(rValue));
// read the current state (initial)
for (DataPointInfo dp : this.datapoint2Notification.keySet())
{
this.network.read(dp);
}
}
@Override
protected void addToNetworkDriver(DataPointInfo dp)
{
// add the datapoint to the network driver, no further operation needed
this.network.addDriver(dp, this);
}
/*
* (non-Javadoc)
*
* @see
* it.polito.elite.dog.core.library.model.devicecategory.SinglePhaseEnergyMeter
* #updateStatus()
*/
@Override
public void updateStatus()
{
((SinglePhaseEnergyMeter) this.device).updateStatus();
}
}