package nl.wldelft.timeseriesparsers; import nl.wldelft.util.DateUtils; import nl.wldelft.util.IOUtils; import nl.wldelft.util.io.BinaryParser; import nl.wldelft.util.io.LittleEndianDataInputStream; import nl.wldelft.util.timeseries.DefaultTimeSeriesHeader; import nl.wldelft.util.timeseries.TimeSeriesContentHandler; import org.apache.log4j.Logger; import java.io.BufferedInputStream; import java.io.EOFException; import java.io.IOException; /** * TimeSeries reader for Keller AG *.IDC files * */ public class IdcTimeSeriesParser implements BinaryParser<TimeSeriesContentHandler> { private static final Logger log = Logger.getLogger(IdcTimeSeriesParser.class); // Constanten private static final int DF_ID_FILE = 0; private static final int DF_ID_DEVICE = 1; private static final int DF_ID_DATA = 2; private static final int DF_ID_UNITS = 3; private static final int DF_ID_PROFILE = 4; private static final int DF_ID_CONFIG = 5; private static final int DF_ID_WL_CONVERTED = 6; private static final int DF_ID_AIR_COMPENSATED = 7; private static final int DF_ID_INFO = 8; // private LittleEndianDataInputStream is = null; private String locationId; /** * Parse Keller AG *.idc bestand * * @param inputStream * @param virtualFileName * @param contentHandler * @throws Exception */ @Override public void parse(BufferedInputStream inputStream, String virtualFileName, TimeSeriesContentHandler contentHandler) throws Exception { int rawTimeZoneOffset = contentHandler.getDefaultTimeZone().getRawOffset(); this.is = new LittleEndianDataInputStream(inputStream); boolean abVersion0310 = false; short amountOfUnits = 0; short availableChannels = 0; float[] userValArr = new float[12]; DefaultTimeSeriesHeader header = new DefaultTimeSeriesHeader(); // Continue to read lines while // there are still some left to read while (true) { // Read block ID short blockId; try { blockId = readBlock(); } catch (EOFException e) { return; } switch (blockId) { case DF_ID_FILE: // File Identification String version = readString(); //log.debug(version); break; case DF_ID_DEVICE: // Device properties int lw = this.is.readInt(); int w1 = lw / 65536; int w2 = lw % 65536; int klasse = w1 / 256; int groep = w1 % 256; int jaar = w2 / 256; int week = w2 % 256; if (jaar == 3) { if (week >= 10) { abVersion0310 = true; } } if (jaar > 3) { abVersion0310 = true; } int serialNumber = this.is.readInt(); boolean configuredAsWaterlevel = this.is.readBoolean(); locationId = readString(); log.debug(locationId); String comment = readString(); log.debug(comment); break; case DF_ID_DATA: // Data records int z = this.is.readInt(); if (z > 0) { for (int i = 0; i < z; i++) { // datum 8 bytes double // channel 1 byte // 3 bytes skip // value 4 bytes float // lw 4 bytes int // 4 bytes skip double doubleTime = is.readDouble(); long time = DateUtils.toTimeFromMicrosoftComDate(doubleTime); contentHandler.setTime(time); byte channel = this.is.readByte(); contentHandler.setTimeSeriesHeader(channel); IOUtils.skipFully(is, 3); float singleValue = this.is.readFloat(); contentHandler.setValue(singleValue); contentHandler.applyCurrentFields(); int longValue = this.is.readInt(); IOUtils.skipFully(is, 4); } } break; case DF_ID_UNITS: // Units availableChannels = this.is.readShort(); amountOfUnits = this.is.readShort(); log.debug("availableChannels" + availableChannels); log.debug("amountOfUnits" + amountOfUnits); for (int i = 0; i < amountOfUnits; i++) { byte channel = this.is.readByte(); byte[] text = new byte[7]; this.is.read(text, 0, 7); float multiplier = this.is.readFloat(); float offset = this.is.readFloat(); byte[] description = new byte[41]; this.is.read(description, 0, 41); IOUtils.skipFully(is, 3); if ((channel == 0) || (channel == 1) || (channel == 2)) { header.setUnit("bar"); } else { if ((channel == 3) || (channel == 4) || (channel == 5)) { header.setUnit("°C"); } else { header.setUnit(""); } } header.setLocationId(locationId); header.setParameterId(Byte.toString(channel)); contentHandler.createTimeSeriesHeaderAlias(channel, header); } break; case DF_ID_PROFILE: // Read device profile for (int i = 0; i < userValArr.length; i++) { userValArr[i] = this.is.readFloat(); } availableChannels = this.is.readShort(); if ((availableChannels & 2) == 2) { float p1min = this.is.readFloat(); float p1max = this.is.readFloat(); } if ((availableChannels & 4) == 4) { float p2min = this.is.readFloat(); float p2max = this.is.readFloat(); } if ((availableChannels & 8) == 8) { float t1min = this.is.readFloat(); float t1max = this.is.readFloat(); } if ((availableChannels & 16) == 16) { float tob1min = this.is.readFloat(); float tob1max = this.is.readFloat(); } if ((availableChannels & 32) == 32) { float tob2min = this.is.readFloat(); float tob2max = this.is.readFloat(); } break; case DF_ID_CONFIG: // Record configuration int startDate = this.is.readInt(); int stopDate = this.is.readInt(); lw = this.is.readInt(); int recordedChannels = lw / 65536; int recordModus = lw % 65536; float trigger1 = this.is.readFloat(); float trigger2 = this.is.readFloat(); if (abVersion0310) { int recFixCounter = this.is.readInt(); short recModCounter = this.is.readShort(); } else { lw = this.is.readInt(); int recFixCounter = lw / 65536; int tmp = lw % 65536; short recModCounter = (short) tmp; } short sw = this.is.readShort(); int tmp = sw / 256; short recModChannel = (short) tmp; tmp = sw % 256; short recSaveCounter = (short) tmp; short recFastModCounter = this.is.readShort(); boolean recEndless = this.is.readBoolean(); break; case DF_ID_WL_CONVERTED: // Waterlevel converted boolean convertedIntoWaterlevel = is.readBoolean(); break; case DF_ID_AIR_COMPENSATED: // Airpressure compensation boolean airCompensated = is.readBoolean(); break; case DF_ID_INFO: // Additional information byte batteryCapacity = this.is.readByte(); for (int i = 0; i < 10; i++) { int reserve = this.is.readInt(); } // Read CRC16 sum of the whole file short crc16 = is.readShort(); break; } } } /** * Read block identification * @return block identification * @throws IOException */ private short readBlock() throws IOException { short block = this.is.readShort(); short w1 = this.is.readShort(); short w2 = this.is.readShort(); int n = (65536 * w1) + w2; log.debug("Block : " + Short.toString(block)); log.debug("Buffer lengte : " + Integer.toString(n)); return block; } /** * Read a string from file * @return string from file * @throws Exception */ private String readString() throws IOException { String retval = ""; // lees lengte van de string short length = this.is.readShort(); if (length > 0) { // Create the byte array to hold the data byte[] bytes = new byte[length]; int nob = this.is.read(bytes, 0, length); if (nob == length) { retval = new String(bytes); } } return retval; } }