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 TimeSeriesContentHandler contentHandler = null;
private Stringint locationIdrawTimeZoneOffset;
/**
* 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 {
intthis.is rawTimeZoneOffset= =new contentHandler.getDefaultTimeZoneLittleEndianDataInputStream(inputStream).getRawOffset();
this.iscontentHandler = new LittleEndianDataInputStream(inputStream);
contentHandler;
booleanthis.rawTimeZoneOffset abVersion0310 = falsecontentHandler.getDefaultTimeZone().getRawOffset();
shortboolean amountOfUnitsabVersion0310 = 0false;
shortString availableChannelslocationId = 0"";
float[] userValArr = new float[12];
DefaultTimeSeriesHeaderfloat headerinstallationDepth = new DefaultTimeSeriesHeader()0f;
//float ContinueheightOfWellhead to= read0f;
lines while
float offset // there are still some left to read
while (true) {
= 0f;
float waterDensity = 0f;
byte batteryCapacity = 0;
//long ReadstartTime block ID= Long.MIN_VALUE;
DefaultTimeSeriesHeader header = short blockIdnew DefaultTimeSeriesHeader();
DefaultTimeSeriesHeader headerEq = try {new DefaultTimeSeriesHeader();
// Continue to read lines while
blockId = readBlock();
// there are still some left to read
} catch (EOFException e) {
while (true) {
returnis.mark(1);
}
int nextByte switch (blockId) {= is.read();
if (nextByte == case DF_ID_FILE:
-1) break; // EOF
is.reset();
// File Identification
// Read block ID
short blockId String version = readStringreadBlock();
switch (blockId) {
//log.debug(version);
case DF_ID_FILE:
break;
// File Identification
case DF_ID_DEVICE:
String version // Device properties= readString();
int lw = this.is.readInt();
if (log.isDebugEnabled()) log.debug("version = " + version);
int w1 = lw / 65536break;
int w2 = lw % 65536;case DF_ID_DEVICE:
int// klasse = w1 / 256;Device properties
int groeplw = w1 % 256this.is.readInt();
int jaarw1 = w2lw / 25665536;
int weekw2 = w2lw % 25665536;
ifint (jaarklasse == w1 3)/ {256;
int groep = w1 if (week >= 10) {% 256;
int jaar = w2 abVersion0310 = true/ 256;
int week = w2 }% 256;
}
if (jaar == 3) {
if (jaarweek >= 310) {
abVersion0310 = true;
}
}
int serialNumber = this.is.readInt(); }
booleanif configuredAsWaterlevel(jaar = this.is.readBoolean();
> 3) {
locationId abVersion0310 = readString()true;
log.debug(locationId);}
Stringint commentserialNumber = readStringthis.is.readInt();
log.debug(commentboolean configuredAsWaterlevel = this.is.readBoolean();
locationId break= readString();
String comment case DF_ID_DATA:= readString();
// Data recordsif (log.isDebugEnabled()) {
int z = thislog.is.readInt(debug("serial number = " + serialNumber);
if (z > 0) {
log.debug("configured as waterlevel = " + configuredAsWaterlevel);
for (int i = 0; i < z; i++) { log.debug("comment = " + comment);
log.debug("location id = " // datum+ locationId);
8 bytes double
}
// channel 1 bytebreak;
case DF_ID_DATA:
// // 3Data bytes skiprecords
int z = this.is.readInt();
// value 4 bytes float
for (int i = 0; i < z; i++) {
// lw 4 bytes int
// datum 8 bytes double
// // channel 1 4byte
bytes skip
// double doubleTime = is.readDouble();
3 bytes skip
long time = DateUtils.toTimeFromMicrosoftComDate(doubleTime);// value 4 bytes float
// lw contentHandler.setTime(time);
4 bytes int
byte channel = this.is.readByte();
// 4 bytes skip
contentHandler.setTimeSeriesHeader(channel);
// The date is stored as the number of days since 30 Dec 1899. Quite why it is not 31 Dec IOUtils.skipFully(is, 3);
is not clear. 01 Jan 1900 has a days value of 2.
floatdouble singleValuedoubleTime = this.is.readFloatreadDouble();
long time = contentHandlerDateUtils.setValuetoTimeFromMicrosoftComDate(singleValuedoubleTime);
// Bewaar lijst contentHandler.applyCurrentFields();
met tijden voor de reeks met inhangdiepte's
int if longValue(startTime == this.is.readInt();
Long.MIN_VALUE) startTime = time;
IOUtilscontentHandler.skipFullysetTime(is, 4new Long(time));
byte channel }= this.is.readByte();
}
contentHandler.setTimeSeriesHeader(channel);
break;
IOUtils.skipFully(is, 3);
case DF_ID_UNITS:
float singleValue = // Units
this.is.readFloat();
availableChannels = thiscontentHandler.is.readShortsetValue(singleValue);
amountOfUnits = thiscontentHandler.is.readShortapplyCurrentFields();
int longValue = logthis.is.debug("availableChannels" + availableChannelsreadInt();
log.debug("amountOfUnits" + amountOfUnits);
// Skip 4 bytes
for (int i = 0; i < amountOfUnits; i++) { IOUtils.skipFully(is, 4);
}
byte channel = this.is.readByte();
break;
byte[] text = new byte[7];case DF_ID_UNITS:
boolean retval this.is.read(text, 0, 7= parseUnits(locationId, header);
if (!retval){
float multiplier = this.is.readFloat();
log.error("Bestand bevat niet de juiste eenheden");
float offset = this.is.readFloat();
throw new Exception("Bestand bevat byte[]niet descriptionde = new byte[41]juiste eenheden");
}
this.is.read(description, 0, 41);
break;
IOUtils.skipFully(is, 3);
case DF_ID_PROFILE:
if ((channel == 0) || (channel == 1) || (channel == 2)) { // Read device profile
for (int i = 0; i < header.setUnit("bar");userValArr.length; i++) {
} else {
userValArr[i] = this.is.readFloat();
if ((channel == 3) || (channel == 4) || (channel == 5)) {
log.isDebugEnabled()) log.debug("userValArr " + userValArr[i]);
}
installationDepth header.setUnit("°C")= userValArr[2];
if (log.isDebugEnabled()) log.debug("installation depth " + installationDepth);
} else {
heightOfWellhead = userValArr[3];
header.setUnit("");
if (log.isDebugEnabled()) log.debug("Height of wellhead above sea level " + heightOfWellhead);
offset = }userValArr[4];
if (log.isDebugEnabled()) log.debug("Offset " + }offset);
waterDensity header.setLocationId(locationId)= userValArr[5];
header.setParameterId(Byte.toString(channel))if (log.isDebugEnabled()) log.debug("Water density " + waterDensity);
short availableChannels = contentHandler.createTimeSeriesHeaderAlias(channel, headerthis.is.readShort();
}
if ((availableChannels & 2) == 2) {
break;
float p1min case DF_ID_PROFILE:
= this.is.readFloat();
// Read device profile
float p1max = this.is.readFloat();
for (int i = 0; i < userValArr.length; i++) {if (log.isDebugEnabled()) log.debug("P1 min " + p1min);
userValArr[i] = this.is.readFloat(if (log.isDebugEnabled()) log.debug("P1 max " + p1max);
}
if ((availableChannels & 4) = this.is.readShort();
= 4) {
if ((availableChannels &float 2)p2min == 2) {this.is.readFloat();
float p1minp2max = this.is.readFloat();
float p1max = this.is.readFloat(if (log.isDebugEnabled()) log.debug("P2 min " + p2min);
}
if (log.isDebugEnabled()) log.debug("P2 max " + p2max);
if }
if ((availableChannels & 48) == 48) {
float p2mint1min = this.is.readFloat();
float p2maxt1max = this.is.readFloat();
}
if ((availableChannels & 8) == 8) {log.isDebugEnabled()) log.debug("T1 min " + t1min);
float t1min = this.is.readFloat();
float t1max = this.is.readFloat(if (log.isDebugEnabled()) log.debug("T1 max " + t1max);
}
if ((availableChannels & 16) == 16) {
float tob1min = this.is.readFloat();
float tob1max = this.is.readFloat();
}
if (log.isDebugEnabled()) log.debug("TOB1 min " + tob1min);
if ((availableChannels & 32) == 32) {
if (log.isDebugEnabled()) log.debug("TOB1 max " + tob1max);
float tob2min = this.is.readFloat();
}
float tob2max = this.is.readFloat();
if ((availableChannels & 32) == 32) {
}
float tob2min = this.is.readFloat();
break;
float tob2max case DF_ID_CONFIG:
= this.is.readFloat();
if (log.isDebugEnabled()) log.debug("TOB2 min //" Record+ configurationtob2min);
int startDate =if this.is.readInt((log.isDebugEnabled()) log.debug("TOB2 max " + tob2max);
int stopDate = this.is.readInt();}
lw = this.is.readInt()break;
case DF_ID_CONFIG:
int recordedChannels = lw // 65536;Record configuration
int recordModusstartDate = lw % 65536this.is.readInt();
floatint trigger1stopDate = this.is.readFloatreadInt();
floatlw trigger2 = this.is.readFloatreadInt();
ifint (abVersion0310) {
recordedChannels = lw / 65536;
int recFixCounterrecordModus = lw % 65536;
float trigger1 = this.is.readIntreadFloat();
float trigger2 short recModCounter = this.is.readShortreadFloat();
}if else(abVersion0310) {
lwint recFixCounter = this.is.readInt();
intshort recFixCounterrecModCounter = lw / 65536this.is.readShort();
} else {
int tmp = lw % 65536;
lw short recModCounter = (short) tmp= this.is.readInt();
}
int recFixCounter = lw / 65536;
short sw = this.is.readShort();
int tmp = swlw /% 25665536;
short recModChannelrecModCounter = (short) tmp;
tmp = sw % 256;}
short recSaveCountersw = this.is.readShort(short) tmp;
shortint recFastModCountertmp = this.is.readShort() sw / 256;
booleanshort recEndlessrecModChannel = this.is.readBoolean(short) tmp;
tmp = sw % break256;
short recSaveCounter = case DF_ID_WL_CONVERTED:(short) tmp;
// Waterlevel convertedshort recFastModCounter = this.is.readShort();
boolean convertedIntoWaterlevelrecEndless = this.is.readBoolean();
break;
case DF_ID_AIRWL_COMPENSATEDCONVERTED:
// AirpressureWaterlevel compensationconverted
boolean airCompensatedconvertedIntoWaterlevel = is.readBoolean();
break;
case DF_ID_INFOAIR_COMPENSATED:
// AdditionalAirpressure informationcompensation
byteboolean batteryCapacityairCompensated = this.is.readBytereadBoolean();
for (int i = 0; i < 10; i++) {
break;
case DF_ID_INFO:
int reserve = this.is.readInt();
// Additional information
}
batteryCapacity = this.is.readByte();
// Read CRC16 sum of the whole file for (int i = 0; i < 10; i++) {
short crc16 int reserve = this.is.readShortreadInt();
break;}
}
}
// Read CRC16 sum }
of the whole /**file
* Read block identification
* @return block identification
short *crc16 @throws IOException
*/
private short readBlock() throws IOException {
= is.readShort();
break;
}
}
// Inhangdiepte
headerEq.setLocationId(locationId);
contentHandler.setTime(startTime);
headerEq.setParameterId("InstallationDepth");
headerEq.setUnit("m");
contentHandler.setTimeSeriesHeader(headerEq);
contentHandler.setValue(installationDepth);
contentHandler.applyCurrentFields();
headerEq.setParameterId("HeightOfWellhead");
headerEq.setUnit("m");
contentHandler.setTimeSeriesHeader(headerEq);
contentHandler.setValue(heightOfWellhead);
contentHandler.applyCurrentFields();
headerEq.setParameterId("Offset");
headerEq.setUnit("m");
contentHandler.setTimeSeriesHeader(headerEq);
contentHandler.setValue(offset);
contentHandler.applyCurrentFields();
headerEq.setParameterId("WaterDensity");
headerEq.setUnit("kg/m3");
contentHandler.setTimeSeriesHeader(headerEq);
contentHandler.setValue(waterDensity);
contentHandler.applyCurrentFields();
headerEq.setParameterId("BatteryCapacity");
headerEq.setUnit("%");
contentHandler.setTimeSeriesHeader(headerEq);
contentHandler.setValue(batteryCapacity);
contentHandler.applyCurrentFields();
}
/**
* 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;
return block;
}
/**
*
*
*
* @param locationId
* @param header
* @return
* @throws java.io.IOException
*/
private boolean parseUnits(
String locationId,
DefaultTimeSeriesHeader header) throws IOException {
boolean retval = true;
short availableChannels = this.is.readShort();
short amountOfUnits = this.is.readShort();
for (int i = 0; i < amountOfUnits; i++) {
// Kanaal
byte channel = this.is.readByte();
// Eenheid
byte[] bytes = new byte[7];
this.is.read(bytes, 0, 7);
String unit = new String(bytes, 1, bytes[0]);
if(unit.equalsIgnoreCase("m")){
retval = false;
}
header.setUnit(unit);
if (unit.contains("°C")) {
header.setUnit("deg C");
}
// Multiplier
float multiplier = this.is.readFloat();
// Offet
float offset = this.is.readFloat();
// Description
bytes = new byte[41];
this.is.read(bytes, 0, 41);
String description = new String(bytes, 1, bytes[0]);
IOUtils.skipFully(is, 3);
if (log.isDebugEnabled()) {
log.debug("channel " + channel);
log.debug("multiplier " + multiplier);
short block = thislog.is.readShort(debug("offset " + offset);
short w1 = thislog.is.readShort(debug("unit " + unit);
short w2 = this.is.readShort( log.debug("description " + description);
int n = (65536}
* w1) + w2;
logheader.debug("BlocksetLocationId(locationId);
: " + Short header.setParameterId(Byte.toString(blockchannel));
logcontentHandler.debug("Buffer lengte : " + Integer.toString(n));createTimeSeriesHeaderAlias(channel, header);
}
return blockretval;
}
/**
* 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;
}
}
|