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();
    }
}
  • No labels