/* ================================================================== * SDMDatumDataSource.java - 26/01/2016 3:06:48 pm * * Copyright 2007-2016 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.datum.deson.sdm; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.springframework.context.MessageSource; import net.solarnetwork.node.DatumDataSource; import net.solarnetwork.node.MultiDatumDataSource; import net.solarnetwork.node.domain.ACEnergyDatum; import net.solarnetwork.node.domain.ACPhase; import net.solarnetwork.node.domain.GeneralNodeACEnergyDatum; import net.solarnetwork.node.hw.deson.meter.SDMData; import net.solarnetwork.node.hw.deson.meter.SDMSupport; import net.solarnetwork.node.io.modbus.ModbusConnection; import net.solarnetwork.node.io.modbus.ModbusConnectionAction; import net.solarnetwork.node.settings.SettingSpecifier; import net.solarnetwork.node.settings.SettingSpecifierProvider; import net.solarnetwork.node.settings.support.BasicTextFieldSettingSpecifier; /** * {@link DatumDataSource} implementation for {@link GeneralNodeACEnergyDatum} * with the SDM series watt meter. * * <p> * The configurable properties of this class are: * </p> * * <dl class="class-properties"> * <dt>messageSource</dt> * <dd>The {@link MessageSource} to use with * {@link SettingSpecifierProvider}.</dd> * * <dt>sampleCacheMs</dt> * <dd>The maximum number of milliseconds to cache data read from the meter, * until the data will be read from the meter again.</dd> * </dl> * * @author matt * @version 1.1 */ public class SDMDatumDataSource extends SDMSupport implements DatumDataSource<GeneralNodeACEnergyDatum>, MultiDatumDataSource<GeneralNodeACEnergyDatum>, SettingSpecifierProvider { private MessageSource messageSource; private long sampleCacheMs = 5000; private SDMData getCurrentSample() { SDMData currSample; if ( isCachedSampleExpired() ) { try { currSample = performAction(new ModbusConnectionAction<SDMData>() { @Override public SDMData doWithConnection(ModbusConnection conn) throws IOException { if ( sample.getControlDataTimestamp() <= 0 ) { // we need to know what kind of meter we are dealing with sample.readControlData(conn); } sample.readMeterData(conn); return sample.getSnapshot(); } }); if ( log.isTraceEnabled() ) { log.trace(currSample.dataDebugString()); } log.debug("Read SDM data: {}", currSample); } catch ( IOException e ) { throw new RuntimeException( "Communication problem reading from Modbus device " + modbusNetwork(), e); } } else { currSample = sample.getSnapshot(); } return currSample; } private boolean isCachedSampleExpired() { final long lastReadDiff = System.currentTimeMillis() - sample.getMeterDataTimestamp(); if ( lastReadDiff > sampleCacheMs ) { return true; } return false; } @Override public Class<? extends GeneralNodeACEnergyDatum> getDatumType() { return SDMDatum.class; } @Override public GeneralNodeACEnergyDatum readCurrentDatum() { final long start = System.currentTimeMillis(); final SDMData currSample = getCurrentSample(); SDMDatum d = new SDMDatum(currSample, ACPhase.Total); d.setSourceId(getSourceMapping().get(ACPhase.Total)); if ( currSample.getMeterDataTimestamp() >= start ) { // we read from the meter postDatumCapturedEvent(d, ACEnergyDatum.class); } return d; } @Override public Class<? extends GeneralNodeACEnergyDatum> getMultiDatumType() { return SDMDatum.class; } @Override public Collection<GeneralNodeACEnergyDatum> readMultipleDatum() { final long start = System.currentTimeMillis(); final SDMData currSample = getCurrentSample(); final List<GeneralNodeACEnergyDatum> results = new ArrayList<GeneralNodeACEnergyDatum>(4); if ( currSample == null ) { return results; } final boolean postCapturedEvent = (currSample.getMeterDataTimestamp() >= start); if ( isCaptureTotal() || postCapturedEvent ) { SDMDatum d = new SDMDatum(currSample, ACPhase.Total); d.setSourceId(getSourceMapping().get(ACPhase.Total)); if ( postCapturedEvent ) { // we read from the meter postDatumCapturedEvent(d, ACEnergyDatum.class); } if ( isCaptureTotal() ) { results.add(d); } } if ( currSample.supportsPhase(ACPhase.PhaseA) && (isCapturePhaseA() || postCapturedEvent) ) { SDMDatum d = new SDMDatum(currSample, ACPhase.PhaseA); d.setSourceId(getSourceMapping().get(ACPhase.PhaseA)); if ( postCapturedEvent ) { // we read from the meter postDatumCapturedEvent(d, ACEnergyDatum.class); } if ( isCapturePhaseA() ) { results.add(d); } } if ( currSample.supportsPhase(ACPhase.PhaseB) && (isCapturePhaseB() || postCapturedEvent) ) { SDMDatum d = new SDMDatum(currSample, ACPhase.PhaseB); d.setSourceId(getSourceMapping().get(ACPhase.PhaseB)); if ( postCapturedEvent ) { // we read from the meter postDatumCapturedEvent(d, ACEnergyDatum.class); } if ( isCapturePhaseB() ) { results.add(d); } } if ( currSample.supportsPhase(ACPhase.PhaseC) && (isCapturePhaseC() || postCapturedEvent) ) { SDMDatum d = new SDMDatum(currSample, ACPhase.PhaseC); d.setSourceId(getSourceMapping().get(ACPhase.PhaseC)); if ( postCapturedEvent ) { // we read from the meter postDatumCapturedEvent(d, ACEnergyDatum.class); } if ( isCapturePhaseC() ) { results.add(d); } } return results; } // SettingSpecifierProvider @Override public String getSettingUID() { return "net.solarnetwork.node.datum.deson.sdm"; } @Override public String getDisplayName() { return "Deson SDM Series Meter"; } public void setMessageSource(MessageSource messageSource) { this.messageSource = messageSource; } @Override public MessageSource getMessageSource() { return messageSource; } @Override public List<SettingSpecifier> getSettingSpecifiers() { SDMDatumDataSource defaults = new SDMDatumDataSource(); List<SettingSpecifier> results = super.getSettingSpecifiers(); results.add(new BasicTextFieldSettingSpecifier("sampleCacheMs", String.valueOf(defaults.getSampleCacheMs()))); return results; } /** * Get the sample cache maximum age, in milliseconds. * * @return the cache milliseconds */ public long getSampleCacheMs() { return sampleCacheMs; } /** * Set the sample cache maximum age, in milliseconds. * * @param sampleCacheSecondsMs * the cache milliseconds */ public void setSampleCacheMs(long sampleCacheMs) { this.sampleCacheMs = sampleCacheMs; } }