blob: 18aab4a9560f8af3a71c456be4b74fc10db89493 [file] [log] [blame]
/********************************************************************************
* Copyright (c) 2015-2018 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
********************************************************************************/
package org.eclipse.mdm.openatfx.mdf.mdf4;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.SeekableByteChannel;
/**
* <p>
* THE HEADER BLOCK <code>HDBLOCK<code>
* </p>
* The HDBLOCK always begins at file position 64. It contains general
* information about the contents of the measured data file and is the root for
* the block hierarchy.
*
* @author Christian Rechner
*/
class HDBLOCK extends BLOCK {
public static String BLOCK_ID = "##HD";
/** Link section */
// Pointer to the first data group block (DGBLOCK) (can be NIL)
// LINK
private long lnkDgFirst;
// Pointer to first file history block (FHBLOCK)
// There must be at least one FHBLOCK with information about the application
// which created the MDF file.
// LINK
private long lnkFhFirst;
// Pointer to first channel hierarchy block (CHBLOCK) (can be NIL).
// LINK
private long lnkChFirst;
// Pointer to first attachment block (ATBLOCK) (can be NIL)
// LINK
private long lnkAtFirst;
// Pointer to first event block (EVBLOCK) (can be NIL)
// LINK
private long lnkEvFirst;
// Pointer to the measurement file comment (TXBLOCK or MDBLOCK) (can be NIL)
// LINK
private long lnkMdComment;
/** Data section */
// Time stamp at start of measurement in nanoseconds elapsed since 00:00:00
// 01.01.1970 (UTC time or local time,
// depending on "local time" flag, see [UTC]). All time stamps for time
// synchronized master channels or events are
// always relative to this start time stamp.
// UINT64
private long startTimeNs;
// Time zone offset in minutes.
// The value is not necessarily a multiple of 60 and can be negative! For
// the current time zone definitions, it is
// expected to be in the range [-840,840] min.
// For example a value of 60 (min) means UTC+1 time zone = Central European
// Time
// Only valid if "time offsets valid" flag is set in time flags.
// INT16
private short tzOffsetMin;
// Daylight saving time (DST) offset in minutes for start time stamp. During
// the summer months, most regions observe
// a DST offset of 60 min (1 hour).
// Only valid if "time offsets valid" flag is set in time flags.
// INT16
private short dstOffsetMin;
// Time flags
// The value contains the following bit flags (Bit 0 = LSB):
// Bit 0: Local time flag
// If set, the start time stamp in nanoseconds represents the local time
// instead of the UTC time, In this case, time
// zone and DST offset must not be considered (time offsets flag must not be
// set). Should only be used if UTC time
// is unknown.
// If the bit is not set (default), the start time stamp represents the UTC
// time.
// Bit 1: Time offsets valid flag
// If set, the time zone and DST offsets are valid. Must not be set together
// with "local time" flag (mutually
// exclusive).
// If the offsets are valid, the locally displayed time at start of
// recording can be determined
// (after conversion of offsets to ns) by
// Local time = UTC time + time zone offset + DST offset.
// UINT8
private byte timeFlags;
// Time quality class
// 0 = local PC reference time (Default)
// 10 = external time source
// 16 = external absolute synchronized time
// UINT8
private byte timeClass;
// Flags
// The value contains the following bit flags (Bit 0 = LSB):
// Bit 0: Start angle valid flag. If set, the start angle value below is
// valid.
// Bit 1: Start distance valid flag. If set, the start distance value below
// is valid.
// UINT8
private byte flags;
// Start angle in radians at start of measurement (only for angle
// synchronous measurements)
// Only valid if "start angle valid" flag is set.
// REAL
private double startAngleRad;
// Start distance in meters at start of measurement
// (only for distance synchronous measurements)
// Only valid if "start distance valid" flag is set.
// All distance values for distance synchronized master channels
// REAL
private double startDistanceM;
/**
* Constructor.
*
* @param sbc
* The byte channel pointing to the MDF file.
* @param mdfFilePath
* THe path to the MDF file.
*/
public HDBLOCK(SeekableByteChannel sbc) {
super(sbc, 64);
}
public long getLnkDgFirst() {
return lnkDgFirst;
}
private void setLnkDgFirst(long lnkDgFirst) {
this.lnkDgFirst = lnkDgFirst;
}
public long getLnkFhFirst() {
return lnkFhFirst;
}
private void setLnkFhFirst(long lnkFhFirst) {
this.lnkFhFirst = lnkFhFirst;
}
public long getLnkChFirst() {
return lnkChFirst;
}
private void setLnkChFirst(long lnkChFirst) {
this.lnkChFirst = lnkChFirst;
}
public long getLnkAtFirst() {
return lnkAtFirst;
}
private void setLnkAtFirst(long lnkAtFirst) {
this.lnkAtFirst = lnkAtFirst;
}
public long getLnkEvFirst() {
return lnkEvFirst;
}
private void setLnkEvFirst(long lnkEvFirst) {
this.lnkEvFirst = lnkEvFirst;
}
public long getLnkMdComment() {
return lnkMdComment;
}
private void setLnkMdComment(long lnkMdComment) {
this.lnkMdComment = lnkMdComment;
}
public long getStartTimeNs() {
return startTimeNs;
}
private void setStartTimeNs(long startTimeNs) {
this.startTimeNs = startTimeNs;
}
public short getTzOffsetMin() {
return tzOffsetMin;
}
private void setTzOffsetMin(short tzOffsetMin) {
this.tzOffsetMin = tzOffsetMin;
}
public short getDstOffsetMin() {
return dstOffsetMin;
}
private void setDstOffsetMin(short dstOffsetMin) {
this.dstOffsetMin = dstOffsetMin;
}
public byte getTimeFlags() {
return timeFlags;
}
private void setTimeFlags(byte timeFlags) {
this.timeFlags = timeFlags;
}
public byte getTimeClass() {
return timeClass;
}
private void setTimeClass(byte timeClass) {
this.timeClass = timeClass;
}
public byte getFlags() {
return flags;
}
private void setFlags(byte flags) {
this.flags = flags;
}
public double getStartAngleRad() {
return startAngleRad;
}
private void setStartAngleRad(double startAngleRad) {
this.startAngleRad = startAngleRad;
}
public double getStartDistanceM() {
return startDistanceM;
}
private void setStartDistanceM(double startDistanceM) {
this.startDistanceM = startDistanceM;
}
public boolean isLocalTime() {
return BigInteger.valueOf(timeFlags).testBit(0);
}
public boolean isTimeFlagsValid() {
return BigInteger.valueOf(timeFlags).testBit(1);
}
public boolean isStartAngleValid() {
return BigInteger.valueOf(flags).testBit(0);
}
public boolean isStartDistanceValid() {
return BigInteger.valueOf(flags).testBit(1);
}
public BLOCK getMdCommentBlock() throws IOException {
if (lnkMdComment > 0) {
String blockType = getBlockType(sbc, lnkMdComment);
// link points to a MDBLOCK
if (blockType.equals(MDBLOCK.BLOCK_ID)) {
return MDBLOCK.read(sbc, lnkMdComment);
}
// links points to TXBLOCK
else if (blockType.equals(TXBLOCK.BLOCK_ID)) {
return TXBLOCK.read(sbc, lnkMdComment);
}
// unknown
else {
throw new IOException("Unsupported block type for MdComment: " + blockType);
}
}
return null;
}
public ATBLOCK getAtFirstBlock() throws IOException {
if (lnkAtFirst > 0) {
return ATBLOCK.read(sbc, lnkAtFirst);
}
return null;
}
public FHBLOCK getFhFirstBlock() throws IOException {
if (lnkMdComment > 0) {
return FHBLOCK.read(sbc, lnkFhFirst);
}
return null;
}
public CHBLOCK getChFirstBlock() throws IOException {
if (lnkChFirst > 0) {
return CHBLOCK.read(sbc, lnkChFirst);
}
return null;
}
public DGBLOCK getDgFirstBlock() throws IOException {
if (lnkDgFirst > 0) {
return DGBLOCK.read(sbc, lnkDgFirst);
}
return null;
}
public EVBLOCK getEvFirstBlock() throws IOException {
if (lnkEvFirst > 0) {
return EVBLOCK.read(sbc, lnkEvFirst);
}
return null;
}
@Override
public String toString() {
return "HDBLOCK [lnkDgFirst=" + lnkDgFirst + ", lnkFhFirst=" + lnkFhFirst + ", lnkChFirst=" + lnkChFirst
+ ", lnkAtFirst=" + lnkAtFirst + ", lnkEvFirst=" + lnkEvFirst + ", lnkMdComment=" + lnkMdComment
+ ", startTimeNs=" + startTimeNs + ", tzOffsetMin=" + tzOffsetMin + ", dstOffsetMin=" + dstOffsetMin
+ ", timeFlags=" + timeFlags + ", timeClass=" + timeClass + ", flags=" + flags + ", startAngleRad="
+ startAngleRad + ", startDistanceM=" + startDistanceM + "]";
}
/**
* Reads a HDBLOCK from the channel starting at current channel position.
*
* @param sbc
* The channel to read from.
* @return The block data.
* @throws IOException
* The exception.
*/
public static HDBLOCK read(SeekableByteChannel sbc) throws IOException {
HDBLOCK block = new HDBLOCK(sbc);
// read block header
ByteBuffer bb = ByteBuffer.allocate(112);
bb.order(ByteOrder.LITTLE_ENDIAN);
sbc.position(64);
sbc.read(bb);
bb.rewind();
// CHAR 4: Block type identifier
block.setId(MDF4Util.readCharsISO8859(bb, 4));
if (!block.getId().equals(BLOCK_ID)) {
throw new IOException("Wrong block type - expected '" + BLOCK_ID + "', found '" + block.getId() + "'");
}
// BYTE 4: Reserved used for 8-Byte alignment
bb.get(new byte[4]);
// UINT64: Length of block
block.setLength(MDF4Util.readUInt64(bb));
// UINT64: Number of links
block.setLinkCount(MDF4Util.readUInt64(bb));
// LINK: Pointer to the first data group block (DGBLOCK) (can be NIL)
block.setLnkDgFirst(MDF4Util.readLink(bb));
// LINK: Pointer to first file history block (FHBLOCK)
block.setLnkFhFirst(MDF4Util.readLink(bb));
// LINK: Pointer to first channel hierarchy block (CHBLOCK) (can be
// NIL).
block.setLnkChFirst(MDF4Util.readLink(bb));
// LINK: Pointer to first attachment block (ATBLOCK) (can be NIL)
block.setLnkAtFirst(MDF4Util.readLink(bb));
// LINK: Pointer to first event block (EVBLOCK) (can be NIL)
block.setLnkEvFirst(MDF4Util.readLink(bb));
// LINK: Pointer to the measurement file comment (TXBLOCK or MDBLOCK)
// (can be NIL)
block.setLnkMdComment(MDF4Util.readLink(bb));
// UINT64: Time stamp at start of measurement in nanoseconds elapsed
// since 00:00:00 01.01.1970
block.setStartTimeNs(MDF4Util.readUInt64(bb));
// INT16: Time zone offset in minutes.
block.setTzOffsetMin(MDF4Util.readInt16(bb));
// INT16: Daylight saving time (DST) offset in minutes for start time
// stamp.
block.setDstOffsetMin(MDF4Util.readInt16(bb));
// UINT8: Time flags
block.setTimeFlags(MDF4Util.readUInt8(bb));
// UINT8: Time quality class
block.setTimeClass(MDF4Util.readUInt8(bb));
// UINT8: Flags
block.setFlags(MDF4Util.readUInt8(bb));
if (block.getFlags() != 0) {
throw new IOException("HDBLOCK hd_flags!=0 not yet supported");
}
// BYTE: Reserved
bb.get();
// REAL: Start angle in radians at start of measurement (only for angle
// synchronous measurements)
block.setStartAngleRad(MDF4Util.readReal(bb));
// REAL: Start distance in meters at start of measurement
block.setStartDistanceM(MDF4Util.readReal(bb));
return block;
}
}