/* ================================================================
* 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();
}
}
|