package edu.sc.seis.sod.process.waveform; import java.awt.Dimension; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import org.w3c.dom.Element; import edu.iris.Fissures.AuditInfo; import edu.iris.Fissures.IfNetwork.ChannelId; import edu.iris.Fissures.IfSeismogramDC.RequestFilter; import edu.iris.Fissures.model.MicroSecondDate; import edu.iris.Fissures.network.ChannelIdUtil; import edu.iris.Fissures.network.ChannelImpl; import edu.iris.Fissures.seismogramDC.LocalSeismogramImpl; import edu.sc.seis.fissuresUtil.bag.LongShortTrigger; import edu.sc.seis.fissuresUtil.cache.CacheEvent; import edu.sc.seis.fissuresUtil.database.NotFound; import edu.sc.seis.fissuresUtil.display.RecordSectionDisplay; import edu.sc.seis.fissuresUtil.display.configuration.DOMHelper; import edu.sc.seis.fissuresUtil.display.configuration.SeismogramDisplayConfiguration; import edu.sc.seis.fissuresUtil.display.registrar.CustomLayOutConfig; import edu.sc.seis.fissuresUtil.exceptionHandler.GlobalExceptionHandler; import edu.sc.seis.fissuresUtil.hibernate.EventSeismogramFileReference; import edu.sc.seis.fissuresUtil.hibernate.NetworkDB; import edu.sc.seis.fissuresUtil.hibernate.SeismogramFileRefDB; import edu.sc.seis.fissuresUtil.time.ReduceTool; import edu.sc.seis.fissuresUtil.xml.DataSet; import edu.sc.seis.fissuresUtil.xml.DataSetSeismogram; import edu.sc.seis.fissuresUtil.xml.MemoryDataSet; import edu.sc.seis.fissuresUtil.xml.MemoryDataSetSeismogram; import edu.sc.seis.fissuresUtil.xml.StdDataSetParamNames; import edu.sc.seis.fissuresUtil.xml.URLDataSetSeismogram; import edu.sc.seis.sod.ConfigurationException; import edu.sc.seis.sod.CookieJar; import edu.sc.seis.sod.LocalSeismogramArm; import edu.sc.seis.sod.MotionVectorArm; import edu.sc.seis.sod.SodUtil; import edu.sc.seis.sod.Start; import edu.sc.seis.sod.hibernate.RecordSectionItem; import edu.sc.seis.sod.hibernate.SodDB; import edu.sc.seis.sod.status.StringTreeLeaf; import edu.sc.seis.sod.subsetter.Subsetter; import edu.sc.seis.sod.subsetter.eventChannel.EventChannelLogicalSubsetter; import edu.sc.seis.sod.subsetter.eventChannel.EventChannelSubsetter; import edu.sc.seis.sod.subsetter.eventChannel.PassEventChannel; import edu.sc.seis.sod.subsetter.requestGenerator.vector.RequestGeneratorWrapper; import edu.sc.seis.sod.subsetter.requestGenerator.vector.VectorRequestGenerator; public class RSChannelInfoPopulator implements WaveformProcess { public RSChannelInfoPopulator(Element config) throws Exception { initConfig(config); saveSeisToFile = getSeismogramWriter(saveSeisId); } public static final String GENS_POPS_XPATH = "//recordSectionDisplayGenerator | //RSChannelInfoPopulator | //externalWaveformProcess[classname/text() = \"edu.sc.seis.rev.map.RecordSectionAndMapGenerator\"]"; private void initConfig(Element config) throws NoSuchFieldException, ConfigurationException { orientationId = SodUtil.getText(SodUtil.getElement(config, "orientationId")); recordSectionId = SodUtil.getText(SodUtil.getElement(config, "recordSectionId")); saveSeisId = DOMHelper.extractText(config, "writerName", orientationId); if(DOMHelper.hasElement(config, "eventChannelSubsetter")) { channelAcceptor = EventChannelLogicalSubsetter.createSubsetter((Subsetter)SodUtil.load(SodUtil.getFirstEmbeddedElement(SodUtil.getElement(config, "eventChannelSubsetter")), EventChannelLogicalSubsetter.packages)); } else { channelAcceptor = new PassEventChannel(); } if(DOMHelper.hasElement(config, "percentSeisHeight")) { percentSeisHeight = new Double(SodUtil.getText(SodUtil.getElement(config, "percentSeisHeight"))).doubleValue(); } int idealNumberOfSeismograms = 10; if(DOMHelper.hasElement(config, "idealNumberOfSeismograms")) { String idealNumText = SodUtil.getText(SodUtil.getElement(config, "idealNumberOfSeismograms")); idealNumberOfSeismograms = new Integer(idealNumText).intValue(); } int maxNumberOfSeismograms = idealNumberOfSeismograms + 5; if(DOMHelper.hasElement(config, "maxNumberOfSeismograms")) { String maxSeisText = SodUtil.getText(SodUtil.getElement(config, "maximumSeismogramsPerRecordSection")); maxNumberOfSeismograms = new Integer(maxSeisText).intValue(); } if(DOMHelper.hasElement(config, "distanceRange")) { distRange = new RSDistanceRange(SodUtil.getElement(config, "distanceRange")); } if(DOMHelper.hasElement(config, "recordSectionSize")) { int width = new Integer(SodUtil.getText(SodUtil.getElement(SodUtil.getElement(config, "recordSectionSize"), "width"))).intValue(); int height = new Integer(SodUtil.getText(SodUtil.getElement(SodUtil.getElement(config, "recordSectionSize"), "height"))).intValue(); recSecDim = new Dimension(width, height); } if(distRange != null) { spacer = new RecordSectionSpacer(distRange, idealNumberOfSeismograms, maxNumberOfSeismograms); } if(DOMHelper.hasElement(config, "displayConfig")) { displayCreator = SeismogramDisplayConfiguration.create(DOMHelper.getElement(config, "displayConfig")); } } public Dimension getRecSecDimension() { return recSecDim; } public AbstractSeismogramWriter getSeismogramWriter() throws Exception { return saveSeisToFile; } public static AbstractSeismogramWriter getSeismogramWriter(String saveId) throws Exception { String xpath = "//mseedWriter[name/text() = \"" + saveId + "\"] | "+ "//sacWriter[name/text() = \"" + saveId + "\"]"; return extractSaveSeis(xpath, "No Writer element with writerName " + saveId + " found"); } private static AbstractSeismogramWriter extractSaveSeis(String xpath, String errorMsgIfNotFound) throws ConfigurationException { Element saveSeisConf = DOMHelper.extractElement(Start.getConfig(), xpath); if(saveSeisConf == null) { throw new ConfigurationException(errorMsgIfNotFound); } return (AbstractSeismogramWriter) SodUtil.load(saveSeisConf, "waveform"); } public List<MemoryDataSetSeismogram> getDSSForRecordSectionItems(List<RecordSectionItem> rsList, CacheEvent event) throws Exception { MemoryDataSet dataSet = new MemoryDataSet("tmp", "tmp", "tmp", new AuditInfo[0]); dataSet.addParameter(MemoryDataSet.EVENT, event, new AuditInfo[0]); List<MemoryDataSetSeismogram> out = new ArrayList<MemoryDataSetSeismogram>(); for (RecordSectionItem rsi : rsList) { synchronized(this) { URLDataSetSeismogram dss = extractSeismogramsFromDB(rsi); out.add(new MemoryDataSetSeismogram(dss.getSeismograms(), dataSet, dss.getName(), dss.getRequestFilter())); dataSet.addParameter(MemoryDataSet.CHANNEL, rsi.getChannel(), new AuditInfo[0]); } } return out; } public List<MemoryDataSetSeismogram> wrap(List<? extends DataSetSeismogram> dss) throws Exception { List<MemoryDataSetSeismogram> memDss = new ArrayList<MemoryDataSetSeismogram>(dss.size()); for(DataSetSeismogram curr: dss) { try { memDss.add( new MemoryDataSetSeismogram(((URLDataSetSeismogram)curr).getSeismograms(), curr.getDataSet(), curr.getName(), curr.getRequestFilter())); } catch (Exception e) { // oops, skip this one GlobalExceptionHandler.handle("Error loading seismogram, skipping. "+curr, e); } } return memDss; } public WaveformResult accept(CacheEvent event, ChannelImpl chan, RequestFilter[] original, RequestFilter[] available, LocalSeismogramImpl[] seismograms, CookieJar cookieJar) throws Exception { List<RecordSectionItem> best = updateTable(event, chan, original, available, seismograms, cookieJar); boolean out = best.size() != 0; return new WaveformResult(seismograms, new StringTreeLeaf(this, out)); } /** if new channel is in the record section, best RecordSectionItems are returned. If * the new channel does not make the best list, then an empty list is returned. */ public List<RecordSectionItem> updateTable(CacheEvent event, ChannelImpl channel, RequestFilter[] original, RequestFilter[] available, LocalSeismogramImpl[] seismograms, CookieJar cookieJar) throws Exception { if( ! channelAcceptor.accept(event, channel, cookieJar).isSuccess()) { return new ArrayList<RecordSectionItem>(); } if (orientationId.equals("main") && ! channel.get_code().endsWith("Z")) { throw new Exception("Try to put non-Z channel in main record section: "+ChannelIdUtil.toStringNoDates(channel)); } URLDataSetSeismogram[] dss; synchronized(this) { URLDataSetSeismogram chanDSS = SeismogramFileRefDB.getSingleton().getDataSetSeismogram(channel.get_id(), event, ReduceTool.cover(original)); dss = addToCache(event, channel, chanDSS).toArray(new URLDataSetSeismogram[0]); } float ston = 0; Object[] cookieKeys = cookieJar.getKeys(); for (int i = 0; i < cookieKeys.length; i++) { if (cookieKeys[i] instanceof String && ((String)cookieKeys[i]).startsWith(PhaseSignalToNoise.PHASE_STON_PREFIX)) { // found StoN ston = ((LongShortTrigger)cookieJar.get((String)cookieKeys[i])).getValue(); break; } } SodDB soddb = SodDB.getSingleton(); RecordSectionItem current = soddb.getRecordSectionItem(orientationId, recordSectionId, event, channel); if(current == null) { current = new RecordSectionItem(orientationId, recordSectionId, event, channel, ston, false); soddb.put(current); } List<RecordSectionItem> bestRSList = soddb.getBestForRecordSection(orientationId, recordSectionId, event); List<RecordSectionItem> newBestRSList = spacer.spaceOut(soddb.getRecordSectionItemList(orientationId, recordSectionId, event)); if (newBestRSList.contains(current)) { // new rsi made the cut if (newBestRSList.size() <= bestRSList.size()) { // current is in best and knocked one out, should recalculate List<RecordSectionItem> allRSI = soddb.getRecordSectionItemList(orientationId, recordSectionId, event); newBestRSList = spacer.spaceOut(bestRSList); List<RecordSectionItem> needUpdate = new ArrayList<RecordSectionItem>(); for (RecordSectionItem rsi : allRSI) { if (rsi.isInBest() == true && ! newBestRSList.contains(rsi)) { //was in best, but not any more rsi.setInBest(false); needUpdate.add(rsi); } } for (RecordSectionItem rsi : newBestRSList) { if (rsi.isInBest() == false) { // was not in best but is now rsi.setInBest(true); needUpdate.add(rsi); } } for (RecordSectionItem rsi : needUpdate) { soddb.getSession().update(rsi); } } else { // just added a new one, so done current.setInBest(true); soddb.getSession().update(current); } return newBestRSList; } else { // current not good enough, no change return new ArrayList<RecordSectionItem>(); } } public ChannelId[] getChannelIds(List<DataSetSeismogram> dss) throws SQLException, NotFound { ChannelId[] channelIds = new ChannelId[dss.size()]; for(int j = 0; j < dss.size(); j++) { channelIds[j] = dss.get(j).getRequestFilter().channel_id; } return channelIds; } public List<URLDataSetSeismogram> extractSeismograms(CacheEvent event) throws Exception { List<URLDataSetSeismogram> copy = extractSeismogramsFromDB(event); List<URLDataSetSeismogram> out = new ArrayList<URLDataSetSeismogram>(); for (URLDataSetSeismogram urlDSS : copy) { // this is bad, but not sure how to populate the cookie jar if(channelAcceptor.accept(event, (ChannelImpl)urlDSS.getChannel(), new CookieJar()).isSuccess()) { out.add(urlDSS); } } return out; } private synchronized static List<URLDataSetSeismogram> addToCache(CacheEvent event, ChannelImpl chan, URLDataSetSeismogram dss) throws Exception { extractSeismogramsFromDB(event); // make sure cache is for current event lastDSS.get(0).getDataSet().addDataSetSeismogram(dss, new AuditInfo[0]); dss.getDataSet().addParameter(StdDataSetParamNames.CHANNEL, chan, new AuditInfo[0]); lastDSS.add(dss); return extractSeismogramsFromDB(event); } private synchronized static List<URLDataSetSeismogram> extractSeismogramsFromDB(CacheEvent event) throws Exception { if (lastDSS.size() == 0 || lastEvent.getDbid() != event.getDbid()) { // new event logger.debug("Not a repeat event, getting dss from db. lastEvent="+lastEvent+" newEvent="+event); List<EventSeismogramFileReference> seisFileRefs = SeismogramFileRefDB.getSingleton().getSeismogramsForEvent(event); DataSet ds = new MemoryDataSet("fake id", "temp name", "RSChannelInfoPopulator", new AuditInfo[0]); ds.addParameter(StdDataSetParamNames.EVENT, event, new AuditInfo[0]); List<URLDataSetSeismogram> dssList = new ArrayList<URLDataSetSeismogram>(); for (EventSeismogramFileReference esRef : seisFileRefs) { try { ChannelImpl chan = NetworkDB.getSingleton().getChannel(esRef.getNetworkCode(), esRef.getStationCode(), esRef.getSiteCode(), esRef.getChannelCode(), new MicroSecondDate(esRef.getBeginTime())); RequestFilter[] rf = null; if (Start.getWaveformRecipe() instanceof LocalSeismogramArm) { rf = ((LocalSeismogramArm)Start.getWaveformRecipe()).getRequestGenerator().generateRequest(event, chan, null); } else { VectorRequestGenerator vrg = ((MotionVectorArm)Start.getWaveformRecipe()).getRequestGenerator(); if (vrg instanceof RequestGeneratorWrapper) { rf = ((RequestGeneratorWrapper)vrg).getRequestGenerator().generateRequest(event, chan, null); } } URLDataSetSeismogram dss = esRef.getDataSetSeismogram(ds, ReduceTool.cover(rf)); dssList.add(dss); ds.addParameter(StdDataSetParamNames.CHANNEL, chan, new AuditInfo[0]); } catch (NotFound e) { logger.error("no channel in dataset for id=" + esRef.getNetworkCode()+"."+esRef.getStationCode()+"."+esRef.getSiteCode()+"."+esRef.getChannelCode() + " even though seismogram is in dataset. Skipping this seismogram."); continue; } } lastEvent = event; lastDSS = dssList; } List<URLDataSetSeismogram> copy = new ArrayList<URLDataSetSeismogram>(lastDSS.size()); copy.addAll(lastDSS); return copy; } public static URLDataSetSeismogram extractSeismogramsFromDB(RecordSectionItem rsi) throws Exception { DataSet ds = new MemoryDataSet("fake id", "temp name", "RSChannelInfoPopulator", new AuditInfo[0]); ds.addParameter(StdDataSetParamNames.EVENT, rsi.getEvent(), new AuditInfo[0]); ds.addParameter(StdDataSetParamNames.CHANNEL, rsi.getChannel(), new AuditInfo[0]); RequestFilter[] rf = null; if (Start.getWaveformRecipe() == null) {throw new ConfigurationException("WaveformArm is NULL");} if (Start.getWaveformRecipe() instanceof LocalSeismogramArm) { rf = ((LocalSeismogramArm)Start.getWaveformRecipe()).getRequestGenerator().generateRequest(rsi.getEvent(), (ChannelImpl)rsi.getChannel(), null); } else { VectorRequestGenerator vrg = ((MotionVectorArm)Start.getWaveformRecipe()).getRequestGenerator(); if (vrg instanceof RequestGeneratorWrapper) { rf = ((RequestGeneratorWrapper)vrg).getRequestGenerator().generateRequest(rsi.getEvent(), (ChannelImpl)rsi.getChannel(), null); } } return SeismogramFileRefDB.getSingleton().getDataSetSeismogram(rsi.getChannel().getId(), rsi.getEvent(), ReduceTool.cover(rf)); } public RecordSectionDisplay getConfiguredRSDisplay() { RecordSectionDisplay rsDisplay = (RecordSectionDisplay)displayCreator.createDisplay(); if(distRange != null) { CustomLayOutConfig custConfig = new CustomLayOutConfig(distRange.getMinDistance(), distRange.getMaxDistance(), percentSeisHeight); custConfig.setSwapAxes(rsDisplay.getSwapAxes()); rsDisplay.setLayout(custConfig); } return rsDisplay; } public static ChannelId getMatchingChanIdIgnoreDates(ChannelId chan, ChannelId[] channels) { for(int i = 0; i < channels.length; i++) { if(ChannelIdUtil.areEqualExceptForBeginTime(chan, channels[i])) { if(!ChannelIdUtil.areEqual(chan, channels[i])) { logger.debug("seismogram channel " + ChannelIdUtil.toString(chan) + " has a different start time than dataset channel " + ChannelIdUtil.toString(channels[i])); } return channels[i]; } } return null; } public static ChannelId getMatchingChanIdByStationCode(ChannelId chan, ChannelId[] channels) { for(int i = 0; i < channels.length; i++) { if(channels[i].station_code.equals(chan.station_code) && channels[i].channel_code.equals(chan.channel_code)) { if(!ChannelIdUtil.areEqual(chan, channels[i])) { logger.debug("seismogram channel " + ChannelIdUtil.toString(chan) + " is not totally equal to dataset channel " + ChannelIdUtil.toString(channels[i])); } return channels[i]; } } return null; } public String getOrientationId() { return orientationId; } public String getRecordSectionId() { return recordSectionId; } public String getSaveSeisId() { return saveSeisId; } private AbstractSeismogramWriter saveSeisToFile; private String orientationId, saveSeisId, recordSectionId; private RSDistanceRange distRange;// = new DistanceRange(0, 180); private double percentSeisHeight = 10; private Dimension recSecDim = new Dimension(500, 500); protected RecordSectionSpacer spacer; private SeismogramDisplayConfiguration displayCreator; private EventChannelSubsetter channelAcceptor; private static CacheEvent lastEvent = null; private static List<URLDataSetSeismogram> lastDSS = new ArrayList<URLDataSetSeismogram>(); private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(RSChannelInfoPopulator.class); }