/* ================================================================== * PM3200ConsumptionDatumDataSource.java - 1/03/2014 8:42:02 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.consumption.schneider.pm3200; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; 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.schneider.meter.PM3200Data; import net.solarnetwork.node.hw.schneider.meter.PM3200Support; 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; import org.springframework.context.MessageSource; /** * DatumDataSource for GeneralNodeACEnergyDatum with the Schneider Electric * PM3200 series kWh 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 2.0 */ public class PM3200ConsumptionDatumDataSource extends PM3200Support implements DatumDataSource<GeneralNodeACEnergyDatum>, MultiDatumDataSource<GeneralNodeACEnergyDatum>, SettingSpecifierProvider { private MessageSource messageSource; private long sampleCacheMs = 5000; private PM3200Data getCurrentSample() { PM3200Data currSample; if ( isCachedSampleExpired() ) { try { currSample = performAction(new ModbusConnectionAction<PM3200Data>() { @Override public PM3200Data doWithConnection(ModbusConnection conn) throws IOException { sample.readMeterData(conn); return new PM3200Data(sample); } }); if ( log.isTraceEnabled() ) { log.trace(currSample.dataDebugString()); } log.debug("Read PM3200 data: {}", currSample); } catch ( IOException e ) { throw new RuntimeException("Communication problem reading from Modbus device " + modbusNetwork(), e); } } else { currSample = new PM3200Data(sample); } return currSample; } private boolean isCachedSampleExpired() { final long lastReadDiff = System.currentTimeMillis() - sample.getDataTimestamp(); if ( lastReadDiff > sampleCacheMs ) { return true; } return false; } @Override public Class<? extends GeneralNodeACEnergyDatum> getDatumType() { return PM3200ConsumptionDatum.class; } @Override public GeneralNodeACEnergyDatum readCurrentDatum() { final long start = System.currentTimeMillis(); final PM3200Data currSample = getCurrentSample(); PM3200ConsumptionDatum d = new PM3200ConsumptionDatum(currSample, ACPhase.Total); d.setSourceId(getSourceMapping().get(ACPhase.Total)); if ( currSample.getDataTimestamp() >= start ) { // we read from the meter postDatumCapturedEvent(d, ACEnergyDatum.class); } return d; } @Override public Class<? extends GeneralNodeACEnergyDatum> getMultiDatumType() { return PM3200ConsumptionDatum.class; } @Override public Collection<GeneralNodeACEnergyDatum> readMultipleDatum() { final long start = System.currentTimeMillis(); final PM3200Data currSample = getCurrentSample(); final List<GeneralNodeACEnergyDatum> results = new ArrayList<GeneralNodeACEnergyDatum>(4); if ( currSample == null ) { return results; } final boolean postCapturedEvent = (currSample.getDataTimestamp() >= start); if ( isCaptureTotal() || postCapturedEvent ) { PM3200ConsumptionDatum d = new PM3200ConsumptionDatum(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 ( isCapturePhaseA() || postCapturedEvent ) { PM3200ConsumptionDatum d = new PM3200ConsumptionDatum(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 ( isCapturePhaseB() || postCapturedEvent ) { PM3200ConsumptionDatum d = new PM3200ConsumptionDatum(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 ( isCapturePhaseC() || postCapturedEvent ) { PM3200ConsumptionDatum d = new PM3200ConsumptionDatum(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.consumption.schneider.pm3200"; } @Override public String getDisplayName() { return "PM3200 Series Meter"; } public void setMessageSource(MessageSource messageSource) { this.messageSource = messageSource; } @Override public MessageSource getMessageSource() { return messageSource; } @Override public List<SettingSpecifier> getSettingSpecifiers() { PM3200ConsumptionDatumDataSource defaults = new PM3200ConsumptionDatumDataSource(); List<SettingSpecifier> results = super.getSettingSpecifiers(); results.add(new BasicTextFieldSettingSpecifier("sampleCacheMs", String.valueOf(defaults .getSampleCacheMs()))); return results; } /** * Get the sample cache maximum age, in seconds. * * @return the cache seconds * @deprecated use {@link #getSampleCacheMs()} */ @Deprecated public int getSampleCacheSeconds() { return (int) (getSampleCacheMs() / 1000); } /** * Set the sample cache maximum age, in seconds. * * @param sampleCacheSeconds * the cache seconds * @deprecated use {@link #setSampleCacheMs(long)} */ @Deprecated public void setSampleCacheSeconds(int sampleCacheSeconds) { setSampleCacheMs(sampleCacheSeconds * 1000L); } public long getSampleCacheMs() { return sampleCacheMs; } public void setSampleCacheMs(long sampleCacheMs) { this.sampleCacheMs = sampleCacheMs; } }