/* ================================================================ * Delft FEWS * ================================================================ * * Project Info: http://www.wldelft.nl/soft/fews/index.html * Project Lead: Karel Heynert (karel.heynert@wldelft.nl) * * (C) Copyright 2003, by WL | Delft Hydraulics * P.O. Box 177 * 2600 MH Delft * The Netherlands * http://www.wldelft.nl * * DELFT-FEWS is a sophisticated collection of modules designed * for building a FEWS customised to the specific requirements * of individual agencies. An open modelling approach allows users * to add their own modules in an efficient way. * * ---------------------------------------------------------------- * DiverMonTimeSeriesParser.java * ---------------------------------------------------------------- * (C) Copyright 2003, by WL | Delft Hydraulics * * Original Author: Stephan Zuiderwijk */ package nl.wldelft.timeseriesparsers; import nl.wldelft.util.StringArrayUtils; import nl.wldelft.util.TextUtils; import nl.wldelft.util.io.IniFile; import nl.wldelft.util.io.LineReader; import nl.wldelft.util.io.TextParser; import nl.wldelft.util.timeseries.DefaultTimeSeriesHeader; import nl.wldelft.util.timeseries.TimeSeriesContentHandler; import org.apache.log4j.Logger; import java.io.BufferedReader; import java.io.StringReader; import java.util.ArrayList; import java.util.List; /** * Parser for Diver *.mon files */ public class DiverMonTimeSeriesParser implements TextParser<TimeSeriesContentHandler> { private static final Logger log = Logger.getLogger(DiverMonTimeSeriesParser.class); /* headers are written in dutch and english */ private enum DiverHeaderConstants { DUTCH_HEADER_CONSTANTS("Instrument info", "Instrument info from data header", "Kanaal", "Locatie", "Identificatie", "Bereik", "Data"), ENGLISH_HEADER_CONSTANTS("Instrument info", "Instrument info from data header", "Channel", "Location", "Identification", "Range", "Data"), LOGGER_HEADER_CONSTANTS("Logger settings", "Series settings", "Channel", "Location", "Identification", "Range", "Data"), MIXED_HEADER_CONSTANTS("Instrument info", "Instrument info from data header", "Kanaal", "Location", "Identification", "Range", "Data"), ENGLISH_MIXED_HEADER_CONSTANTS("Instrument info", "Instrument info from data header", "Channel", "Location", "Identification", "Range", "Data"), INCOMPLETE_HEADER_CONSTANTS("Instrument info", "Instrument info from data header", "Kanaal", "Locatie", "Identificatie", "Bereik", "Data"); String loggerSettings; String seriesSettings; String channel; String location; String chnlId; String chnlRange; String data; DiverHeaderConstants(String loggerSettings, String seriesSettings, String channel, String location, String chnlId, String chnlRange, String data) { this.location = location; this.seriesSettings = seriesSettings; this.chnlId = chnlId; this.chnlRange = chnlRange; this.loggerSettings = loggerSettings; this.channel = channel; this.data = data; } } private LineReader reader = null; private String virtualFileName = null; private TimeSeriesContentHandler contentHandler = null; private int channelCount = -1; @Override public void parse(LineReader reader, String virtualFileName, TimeSeriesContentHandler contentHandler) throws Exception { this.reader = reader; this.virtualFileName = virtualFileName; this.contentHandler = contentHandler; this.contentHandler.addMissingValue(99999f); reader.setSkipEmptyLines(true); parseHeaders(); parseData(); } /** * Parses the datarows of the diver file. Data is written as a timestamp, followed by a number of columns. Each * column refers to a channel that is declared in the Series settings * */ private void parseData() throws Exception { int nrOfLines = Integer.parseInt(reader.readLine().trim()); long lineCounter = 0; char columnSeparator = '\0'; String[] buffer = new String[channelCount + 2]; for (String line; (line = reader.readLine()) != null;) { if (columnSeparator == '\0') { columnSeparator = line.indexOf('\t') == -1 ? ' ' : '\t'; } if (line.startsWith("END OF DATA")) break; TextUtils.split(line, columnSeparator, buffer); contentHandler.setTime(contentHandler.getDefaultTimeZone(), "yyyy/MM/dd", buffer[0], "HH:mm:ss", buffer[1]); for (int i = 0; i < channelCount; i++) { String valueText = buffer[i + 2]; if (valueText == null) continue; valueText = valueText.replace(',', '.'); contentHandler.setTimeSeriesHeader(i); contentHandler.setValue('.', valueText); contentHandler.applyCurrentFields(); } lineCounter++; } if (log.isDebugEnabled()) log.debug("Number of expected values: " + nrOfLines + " Found values: " + lineCounter); } /** * Parses the available channels from the *.mon file. Multiple channels can exist in a file. Typically the number * of channels will be 2 but files with 1 channel do exist. */ private void parseHeaders() throws Exception { IniFile iniFile = parseHeaderIniFile(); DiverHeaderConstants headerConstants = determineLanguage(iniFile); String loc = iniFile.getValue(headerConstants.loggerSettings, headerConstants.location); if (TextUtils.trimToNull(loc) == null) { throw new Exception("No location found"); } DefaultTimeSeriesHeader header = new DefaultTimeSeriesHeader(); String[] subjects = iniFile.getSubjects(); channelCount = 0; //loop over all subjects of the form "Channel" + i + " from data header", //i.e. loop over the headers for all parameters. for (int i = 1; ; i++) { String subject = headerConstants.channel.toLowerCase() + ' ' + i + " from data header"; if (StringArrayUtils.indexOfIgnoreCase(subjects, subject) == -1) { if (this.channelCount == 0) { throw new Exception("No channel identification found"); } return; } // subject is found, get id and range String id = iniFile.getValue(subject, headerConstants.chnlId); if (id.length() == 0) { log.warn("Identification for channel: '" + subject + "' is not set in File: " + virtualFileName); // set dummy parameter id = "Not Defined"; } header.setParameterId(id); header.setLocationId(loc); contentHandler.createTimeSeriesHeaderAlias(channelCount, header); channelCount++; } } private IniFile parseHeaderIniFile() throws Exception { List<String> headerLines = new ArrayList<String>(); for (String line; (line = reader.readLine()) != null;) { if (line.trim().equalsIgnoreCase("[Data]")) { BufferedReader reader = new BufferedReader(new StringReader(TextUtils.join(headerLines, '\n'))); return new IniFile(reader, virtualFileName); } headerLines.add(line); } throw new Exception("No data found"); } private static DiverHeaderConstants determineLanguage(IniFile iniFile) throws Exception { String[] subjects = iniFile.getSubjects(); if (subjects.length > 0 && subjects[0].equalsIgnoreCase("Logger settings")) return DiverHeaderConstants.LOGGER_HEADER_CONSTANTS; String subject2 = subjects.length < 2 ? "" : subjects[1]; if (subject2.equalsIgnoreCase("channel 1")) return DiverHeaderConstants.ENGLISH_HEADER_CONSTANTS; if (subject2.equalsIgnoreCase("kanaal 1")) return DiverHeaderConstants.DUTCH_HEADER_CONSTANTS; if (subject2.equalsIgnoreCase("channel 1 from data header")) return DiverHeaderConstants.ENGLISH_HEADER_CONSTANTS; if (subject2.equalsIgnoreCase("kanaal 1 from data header")) return DiverHeaderConstants.DUTCH_HEADER_CONSTANTS; if (subjects.length > 5) { if (subjects[4].equalsIgnoreCase("kanaal 1 from data header")) return DiverHeaderConstants.MIXED_HEADER_CONSTANTS; if (subjects[4].equalsIgnoreCase("kanaal1 from data header")) return DiverHeaderConstants.MIXED_HEADER_CONSTANTS; if (subjects[4].equalsIgnoreCase("channel 1 from data header")) return DiverHeaderConstants.ENGLISH_MIXED_HEADER_CONSTANTS; if (subjects[4].equalsIgnoreCase("channel1 from data header")) return DiverHeaderConstants.ENGLISH_MIXED_HEADER_CONSTANTS; } if (subjects.length > 3) { if (subjects[2].equalsIgnoreCase("kanaal 1 from data header")) return DiverHeaderConstants.INCOMPLETE_HEADER_CONSTANTS; } // the file format is corrupt log.error("\n" + subjects.length + " strings read from the header : " + composeMessage(subjects) + "\nexpected in string nr.2: 'channel 1' or 'kanaal 1' or 'channel 1 from data header' or 'kanaal 1 from data header' or" + "\nexpected in string nr.3: 'kanaal 1 from data header' or" + "\nexpected in string nr.5: 'kanaal 1 from data header' or 'kanaal1 from data header'"); throw new Exception("File format not recognized as a valid diver (.mon) file, check the headers"); } private static String composeMessage(String[] substr) { StringBuffer buffer = new StringBuffer(); for (int i = 0; i < substr.length; i++) { buffer.append("'"); buffer.append(substr[i]); buffer.append("' "); } return buffer.toString(); } }