/* ===================================================================
* OutbackMX60PowerDatumDataSource.java
*
* Created Aug 7, 2008 9:48:26 PM
*
* Copyright (c) 2008 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.power.impl;
import net.solarnetwork.node.DataCollector;
import net.solarnetwork.node.DataCollectorFactory;
import net.solarnetwork.node.DatumDataSource;
import net.solarnetwork.node.power.PowerDatum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.ObjectFactory;
/**
* Implementation of {@link net.solarnetwork.node.DatumDataSource} for
* {@link PowerDatum} the Outback MX60, communicating via the
* {@code DataCollector} serial API.
*
* <p>
* Serial parameters known to work are:
* </p>
*
* <dl>
* <dt>bufferSize</dt>
* <dd>64</dt>
*
* <dt>magic</dt>
* <dd>The address of the MX60, e.g. <em>A</em>.</dd>
*
* <dt>readSize</dt>
* <dd>47</dt>
*
* <dt>maxWait</dt>
* <dd>60000</dt>
* </dl>
*
* <p>
* The configurable properties of this class are:
* </p>
*
* <dl class="class-properties">
* <dt>dataCollectorFactory</dt>
* <dd>The {@link DataCollectorFactory} to use to obtain {@link DataCollector}
* instances for reading the Outback MX60 data.</dd>
* </dl>
*
* @author matt
* @version 1.1
*/
public class OutbackMX60PowerDatumDataSource implements DatumDataSource<PowerDatum> {
private static final int FRAME_IDX_DC_AMPS = 2;
private static final int FRAME_IDX_PV_AMPS = 3;
private static final int FRAME_IDX_PV_VOLTS = 4;
private static final int FRAME_IDX_KWATT_HOURS = 5;
private static final int FRAME_IDX_BAT_VOLTS = 10;
private static final float BAT_VOLTS_MULTIPLIER = 0.1F;
private static final double KWATT_HOURS_MULTIPLIER = 0.1;
private ObjectFactory<DataCollector> dataCollectorFactory;
private final Logger log = LoggerFactory.getLogger(getClass());
@Override
public Class<? extends PowerDatum> getDatumType() {
return PowerDatum.class;
}
@Override
public PowerDatum readCurrentDatum() {
DataCollector dataCollector = null;
String data = null;
try {
dataCollector = this.dataCollectorFactory.getObject();
dataCollector.collectData();
data = dataCollector.getCollectedDataAsString();
} finally {
if ( dataCollector != null ) {
dataCollector.stopCollecting();
}
}
if ( data == null ) {
log.warn("Null serial data received, serial communications problem");
return null;
}
if ( log.isDebugEnabled() ) {
log.debug("Collected serial data: " + data);
}
return getPowerDatumInstance(data);
}
/**
* Parse an Outback Mate serial data string into a PowerDatum object.
*
* <p>
* The string data format looks like
* {@code D,00,00,00,016,129,00,00,000,00,511,000,000,046}. See the
* <em>Mate Serial Communications Guide</em> for details.
* </p>
*
* @param data
* the serial data string
* @return a PowerDatum instance
*/
@SuppressWarnings("deprecation")
private PowerDatum getPowerDatumInstance(String data) {
// split data on comma
String[] frame = data.split(",");
if ( frame.length < 14 ) {
if ( log.isWarnEnabled() ) {
log.warn("Expected 14 data elements, but got " + frame.length);
}
return null;
}
PowerDatum pd = new PowerDatum();
Double d = getFrameDouble(frame, FRAME_IDX_PV_AMPS);
if ( d != null ) {
pd.setPvAmps(d.floatValue());
}
d = getFrameDouble(frame, FRAME_IDX_PV_VOLTS);
if ( d != null ) {
pd.setPvVolts(d.floatValue());
}
d = getFrameDouble(frame, FRAME_IDX_DC_AMPS);
if ( d != null ) {
pd.setDcOutputAmps(d.floatValue());
}
d = getFrameDouble(frame, FRAME_IDX_BAT_VOLTS);
if ( d != null ) {
pd.setBatteryVolts(d.floatValue() * BAT_VOLTS_MULTIPLIER);
}
d = getFrameDouble(frame, FRAME_IDX_KWATT_HOURS);
if ( d != null ) {
pd.setKWattHoursToday(d * KWATT_HOURS_MULTIPLIER);
}
return pd;
}
private Double getFrameDouble(String[] frame, int idx) {
if ( frame[idx].length() > 0 ) {
return Double.valueOf(frame[idx]);
}
return null;
}
@Override
public String getUID() {
return getClass().getName();
}
@Override
public String getGroupUID() {
return null;
}
public ObjectFactory<DataCollector> getDataCollectorFactory() {
return dataCollectorFactory;
}
public void setDataCollectorFactory(ObjectFactory<DataCollector> dataCollectorFactory) {
this.dataCollectorFactory = dataCollectorFactory;
}
}