blob: a65bdbfc79af37bc59035b8157c9e40d0354a322 [file] [log] [blame]
package org.eclipse.mdm.openatfx.mdf.mdf4;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.SeekableByteChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class CABLOCK extends BLOCK
{
public static final String BLOCK_ID = "##CA";
/**
* Array of composed elements: Pointer to a CNBLOCK for array of structures, or to a CABLOCK for array of arrays (can be NIL).
* If a CABLOCK is referenced, it must use the "CN template" storage type (ca_storage = 0).
*/
private long lnkComposition;
/**
* List of links to either a data block (DTBLOCK or DZBLOCK for this block type) or a data list block (DLBLOCK of data blocks or its HLBLOCK) for each element.
* <p>
* Only present for storage type "DG template" (ca_storage = 2)!
* </p>
* <p>
* A link in this list may only be NIL if the cycle count of the respective element is 0: ca_data[k] = NIL => ca_cycle_count[k] = 0<br>
* The links are stored line-oriented, i.e. element k uses ca_data[k]. The size of the list must be equal to Π N(d),
* i.e. to the product of the number of elements per dimension N(d) over all dimensions D.<br>
* </p>
* <p>
* Note: link ca_data[0] must be equal to dg_data link of the parent DGBLOCK.
* </p>
*/
private long[] lnksData;
/**
* References to channels for size signal of each dimension (can be NIL).
* <p>
* Only present if "dynamic size" flag (bit 0) is set.
* </p>
* <p>
* Each reference is a link triple with pointer to parent DGBLOCK, parent CGBLOCK and CNBLOCK for the channel
* (either all three links are assigned or NIL). Thus the links have the following order:<br>
* DGBLOCK for size signal of dimension 1<br>
* CGBLOCK for size signal of dimension 1<br>
* CNBLOCK for size signal of dimension 1<br>
* …<br>
* DGBLOCK for size signal of dimension D<br>
* CGBLOCK for size signal of dimension D<br>
* CNBLOCK for size signal of dimension D<br>
* </p>
* <p>
* The size signal can be used to model arrays whose number of elements per dimension can vary over time.
* If a size signal is specified for a dimension, the number of elements for this dimension at some point in time
* is equal to the value of the size signal at this time (i.e. for time-synchronized signals,
* the size signal value with highest time stamp less or equal to current time stamp).
* If the size signal has no recorded signal value for this time (yet), assume 0 as size.
* </p>
* <p>
* Since each referenced channel defines the current size of a dimension, its physical values must be Integer values ≥ 0 and ≤ N(d),
* i.e. the values must not exceed the maximum number of elements for the respective dimension.
* Usually, a size signal should have some Integer data type (cn_data_type ≤ 3) without conversion rule and without unit.
* </p>
* <p>
* Since the size signal and the array signal must be synchronized, their channel groups must contain at least one common master channel type.
* </p>
* <p>
* Note: the "positions" of the array elements are fixed as defined for the maximum allowed array size.
* They will not change when the array is reduced by a size signal value less than N(d).
* However, the "removed" array elements must not be considered.
* For row-oriented CN template storage, the elements would be stored in the record in following order:<br>
* a11, a12, a13, a21, a22, a23<br>
* Assume that the size of the second dimension is reduced from 3 to 2. Then the values of elements a13 and a23
* will be undefined and should not be used. However, the other elements stay at the same position (Byte offset).<br>
* a11, a12, [a13], a21, a22, [a23]
* </p>
*/
private long[] lnksDynamicSize;
/**
* Reference to channels for input quantity signal for each dimension (can be NIL).
* <p>
* Only present if "input quantity" flag (bit 1) is set.
* </p>
* <p>
* Reference to channels for input quantity signal for each dimension (can be NIL).
* Each reference is a link triple with pointer to parent DGBLOCK, parent CGBLOCK and
* CNBLOCK for the channel (either all three links are assigned or NIL). Thus the links have the following order:<br>
* DGBLOCK for input quantity of dimension 1<br>
* CGBLOCK for input quantity of dimension 1<br>
* CNBLOCK for input quantity of dimension 1<br>
* …<br>
* DGBLOCK for input quantity of dimension D<br>
* CGBLOCK for input quantity of dimension D<br>
* CNBLOCK for input quantity of dimension D<br>
* </p>
* <p>
* Since the input quantity signal and the array signal must be synchronized,
* their channel groups must contain at least one common master channel type.
* </p>
* <p>
* The input quantity often is denoted as "working point" because it is used for look-up of a value in the array.<br>
* For array type "look-up", the value of the input quantity will be used for looking up a value in the
* scaling axis of the respective dimension (either axis channel or fixed axis values listed in ca_axis_value list).<br>
* For array type "scaling axis", the value of the input quantity will be used for looking up a value in the axis itself.
* The input quantity should have the same physical unit as the axis it is applied to.
* </p>
*/
private long[] lnksInputQuantity;
/**
* Reference to channel for output quantity (can be NIL).
* <p>
* Only present if "output quantity" flag (bit 2) is set.
* </p>
* <p>
* The reference is a link triple with pointer to parent DGBLOCK, parent CGBLOCK and CNBLOCK for the channel
* (either all three links are assigned or NIL). Since the output quantity signal and the array signal must be synchronized,
* their channel groups must contain at least one common master channel type.
* For array type "look-up", the output quantity is the result of the complete look-up.
* The output quantity should have the same physical unit as the array elements of the array that references it.
* </p>
*/
private long[] lnksOutputQuantity;
/**
* Reference to channel for comparison quantity (can be NIL).
* <p>
* Only present if "comparison quantity" flag (bit 3) is set.
* </p>
* <p>
* The reference is a link triple with pointer to parent DGBLOCK, parent CGBLOCK and CNBLOCK for the channel
* (either all three links are assigned or NIL). Since the comparison quantity signal and the array signal must be synchronized,
* their channel groups must contain at least one common master channel type.
* The comparison quantity should have the same physical unit as the array elements.
* </p>
*/
private long[] lnksComparisonQuantity;
/**
* Pointer to a conversion rule (CCBLOCK) for the axis of each dimension. If a link NIL a 1:1 conversion must be used for this axis.
* <p>
* Only present if "axis" flag (bit 4) is set.
* </p>
* <p>
* If the "fixed axis" flag (Bit 5) is set, the conversion must be applied to the fixed axis values of the respective axis/dimension
* (ca_axis_value list stores the raw values as REAL). If the link to the CCBLOCK is NIL already the physical values are stored
* in the ca_axis_value list. If the "fixed axes" flag (Bit 5) is not set, the conversion must be applied to the raw values
* of the respective axis channel, i.e. it overrules the conversion specified for the axis channel, even if the ca_axis_conversion link is NIL!<br>
* </p>
* <p>
* Note: ca_axis_conversion may reference the same CCBLOCK as referenced by the respective axis channel ("sharing" of CCBLOCK).
* </p>
*/
private long[] lnksConversionRule;
/**
* References to channels for axis of respective dimension (can be NIL).
* <p>
* Only present if "axis" flag (bit 4) is set and "fixed axes flag" (bit 5) is not set.
* </p>
* <p>
* Each reference is a link triple with pointer to parent DGBLOCK, parent CGBLOCK and CNBLOCK for the channel
* (either all three links are assigned or NIL). Thus the links have the following order:<br>
* DGBLOCK for axis of dimension 1<br>
* CGBLOCK for axis of dimension 1<br>
* CNBLOCK for axis of dimension 1<br>
* …<br>
* DGBLOCK for axis of dimension D<br>
* CGBLOCK for axis of dimension D<br>
* CNBLOCK for axis of dimension D<br>
* </p>
* <p>
* Each referenced channel must be an array of type "scaling axis" or "interval axis".<br>
* For a "scaling axis" the maximum number of elements (ca_dim_size[0] in axis) must not be less than the maximum number of elements
* of the respective dimension d in the array which references it (ca_dim_size[d-1]).<br>
* For an "interval axis" the number of elements must be exactly one more than respective dimension d
* in the "classification result" array (ca_dim_size[d-1]+1).
* </p>
* <p>
* If an axis signal must be synchronized with the array signal (e.g. for dynamic size axis),
* the channel groups of the axis and the array signal must contain at least one common master channel type.
* If a fixed axis is stored by an axis channel, its channel group should only contain one cycle.
* </p>
* <p>
* If a reference to an axis is NIL, there is no axis and a zero-based index should be used for axis values.
* </p>
*/
private long[] lnksScalingAxis;
/**
* <b>Array type</b> (defines semantic of the array)
* <p>Attention: for some array types certain features are not allowed (see ca_flags)</p>
* <p><ul>
* <li>0 = Array<br>
* The array is a simple D-dimensional value array (value block) without axes and without input/output/comparison quantities.
*
* <li>1 = Scaling axis<br>
* The array is a scaling axis (1-dimensional vector), possibly referenced by one or more arrays.
* If referenced by an array of type "look-up", the axis itself may have a scaling axis (e.g. CURVE_AXIS) and an own input quantity.
*
* <li>2 = Look-up<br>
* The array is a D-dimensional array with axes. It can have input/output/comparison quantities.
*
* <li>3 = Interval axis<br>
* The array is an axis (1-dimensional vector) defining interval ranges as "axis points".
* It can be referenced by one or more arrays of type "classification result" (ca_type = 4).
* The interval axis must have at least two elements (ca_dim_size[0] = N(0) > 1)
* and the physical values of the elements must be strictly monotonous increasing over the element index,
* i.e. ai < ai+1 for 1 ≤ i < N(0)<br>
* In contrast to a scaling axis (ca_type = 1), an interval axis always has one element more than the number of elements
* for the respective dimension of the classification result array which references it.
* The elements of the class interval axis define the borders of interval ranges that are seen as axis points.
* Depending on the "left-open interval" flag (bit 7 in ca_flags), the intervals are defined as<br>
* ]ao,a1], ]a1,a2], …, ]aN(0)-1,aN(0)] (flag set)<br>
* [ao,a1[, [a1,a2[, …, [aN(0)-1,aN(0)[ (flag not set)<br>
* valid since MDF 4.1.0, should not occur for earlier versions
*
* <li>4 = Classification result<br>
* The array is a D-dimensional array containing classification results.
* It can have scaling axes (ca_type = 1) or interval axes (ca_type = 3), even mixed.
* Details about the classification method and parameters can be given as XML tags in cn_md_comment
* of the parent CN block and the CN blocks of the axes.<br>
* valid since MDF 4.1.0, should not occur for earlier versions
* </ul><p>
*/
private byte arrayType;
public static final byte ARRAYTYPE_ARRAY = 1;
public static final byte ARRAYTYPE_SCALING_AXIS = 1 << 1;
public static final byte ARRAYTYPE_LOOKUP = 1 << 2;
public static final byte ARRAYTYPE_INTERVAL_AXIS = 1 << 3;
public static final byte ARRAYTYPE_CLASSIFICATION_RESULT = 1 << 4;
/**
* <b>Storage type</b (defines how the element values are stored)
* <p><ul>
* <li>0 = CN template<br>
* Values of all elements of the array are stored in the same record (i.e. all elements are measured together).
* The parent CNBLOCK defines the first element in the record (k = 0).
* All other elements are defined by the same CNBLOCK except that the values for cn_byte_offset
* and cn_inval_bit_pos change for each component.
*
* <li>1 = CG template<br>
* Value for each element of the array is stored in a separate record (i.e. elements are stored independently of each other).
* All records are stored in the same data block referenced by the parent DGBLOCK.
* As a consequence the data group (and thus the MDF file) is unsorted.
* All elements have exactly the same record layout specified by the parent CGBLOCK.
* However, each element uses a different cycle count (given by ca_cycle_count[k])
* and a different record ID which must be calculated by "auto-increment" of the record ID of the parent CGBLOCK:
* cg_record_id + k.<br> Since ca_cycle_count[0] must be equal to cg_cycle_count of the parent CGBLOCK,
* the parent CNBLOCK of the CABLOCK automatically describes the first array element (k = 0).
* When sorting a data group, a CABLOCK with "CG template" storage will be converted to a CABLOCK with "DG template" storage.
*
* <li>2 = DG template<br>
* Similar to CG template, the value of each element of the array is stored in a separate record (i.e. elements are stored independently of each other).
* However, the records for each element are stored in separate data blocks referenced by the list of links in ca_data.
* Similar to "CG template" storage, all elements have exactly the same record layout (defined by the parent CGBLOCK)
* but a different cycle count (specified by ca_cycle_count[k]).<br>
* Since ca_cycle_count[0] must be equal to cg_cycle_count of the parent CGBLOCK, and ca_data[0] must be equal to
* dg_data of the parent DGBLOCK, the parent CNBLOCK of the CABLOCK automatically describes the first array element (k = 0).
* </ul></p>
*/
private byte storageType;
public static final byte STORAGETYPE_CN_TEMPLATE = 1;
public static final byte STORAGETYPE_CG_TEMPLATE = 1 << 1;
public static final byte STORAGETYPE_DG_TEMPLATE = 1 << 2;
/**
* <b>Flags (0-based!)</b>
* <p> The value contains the following bit flags (Bit 0 = LSB):
* <ul>
* <li>Bit 0: dynamic size flag<br>
* If set, the number of scaling points for the array is not fixed but can vary over time.
* Can only be set for array types "look-up" and "scaling axis".
*
* <li>Bit 1: input quantity flag<br>
* If set, a channel for the input quantity is specified for each dimension by ca_input_quantity.
* Can only be set for array types "look-up" and "scaling axis".
*
* <li>Bit 2: output quantity flag<br>
* If set, a channel for the output quantity is specified by ca_output_quantity. Can only be set for array type "look-up".
*
* <li>Bit 3: comparison quantity flag<br>
* If set, a channel for the comparison quantity is specified by ca_comparison_quantity. Can only be set for array type "look-up".
*
* <li>Bit 4: axis flag<br>
* If set, a scaling axis is given for each dimension of the array, either as fixed or as dynamic axis,
* depending on "fixed axis" flag (bit 5). Can only be set for array types "look-up", "scaling axis" and "classification result".
*
* <li>Bit 5: fixed axis flag<br>
* If set, the scaling axis is fixed and the axis points are stored as raw values in ca_axis_value list.
* If not set, the scaling axis may vary over time and the axis points are stored as channel referenced in ca_axis for each dimension.
* Only relevant if "axis" flag (bit 4) is set. Can only not be set for array types "look-up" and "scaling axis".
*
* <li>Bit 6: inverse layout flag<br>
* If set, the record layout is "column oriented" instead of "row oriented".
* Only relevant for "CN template" storage type and for number of dimensions > 1.
*
* <li>Bit 7: left-open interval flag<br>
* If set, the interval ranges for the class interval axes are left-open and right-closed, i.e. ]a,b] = {x | a < x ≤ b}.<br>
* If not set, the interval ranges for the class interval axes are left-closed and right-open, i.e. [a,b[ = {x | a ≤ x < b}.<br>
* Note that opening or closing is irrelevant for infinite endpoints (there can only be -INF for the left border of the
* very first interval, or +INF for right border of the very last interval).
* Only relevant for array type "interval axes".<br>
* valid since MDF 4.1.0, should not be set for earlier versions
* </ul></p>
*/
private long flags;
public static final byte FLAG_DYNAMIC_SIZE = 1;
public static final byte FLAG_INPUT_QUANTITY_DEFINED = 1 << 1;
public static final byte FLAG_OUTPUT_QUANTITY_DEFINED = 1 << 2;
public static final byte FLAG_COMPARISON_QUANTITY_DEFINED = 1 << 3;
public static final byte FLAG_SCALING_AXIS_DEFINED = 1 << 4;
public static final byte FLAG_FIXED_AXES = 1 << 5;
public static final byte FLAG_INVERSE_LAYOUT = 1 << 6;
public static final byte FLAG_LEFT_OPEN_INTERVALS = -128;
/**
* Base factor for calculation of Byte offsets for "CN template" storage type.
* <p>The absolute value of ca_byte_offset_base should be larger than or equal to the size of Bytes required
* to store a component channel value in the record (all must have the same size).
* If the values are equal, then the component values are stored contiguously, i.e.next to each other without gaps.
* If ca_byte_offset_base is negative, the component values are stored with decreasing index.</p>
*/
private long byteOffsetBase;
/**
* Base factor for calculation of invalidation bit positions for CN template storage type.
* <p>For a simple array which is not part of a nested composition,
* ca_inval_bit_pos_base = 1 means that the invalidation bits for the component channels are stored without gaps and
* ca_inval_bit_pos_base = 0 means that all component channels use the same invalidation bit in the record.
*/
private long invalBitPosBase;
/**
* Maximum number of elements for each dimension N(d).
* <p>N(d) > 0 for all d. <br>For array type "interval axis", there must be at least two elements, i.e. N(0) > 1.</p>
*/
private long[] dimSizes;
/**
* Number of dimensions D > 0<br>
* For array type "scaling axis" or "interval axis", D must be 1.
*/
private int nrOfDimensions;
/**
* List of raw values for (fixed) axis points of each axis.
* <p>Only present if "fixed axes" flag (bit 5) is set!</p>
* <p>
* List of raw values for (fixed) axis points of each axis. The axis points are stored in a linear sequence for each dimension:<br>
* A1(1), A1(2), ..., A1(N(1)),<br>
* A2(1), A2(2), ..., A2(N(2)),<br>
* ...<br>
* AD(1), AD(2), ..., AD(N(D))<br>
* where Ad(j) is axis point j of dimension d and N(d) is the number of elements for dimension d.<br>
* Example for D = 2 dimensions (as explained later Y axis for A1 and X axis for A2):<br>
* Y(1), Y(2), ..., Y(N(1)),<br>
* X(1), X(2), ..., X(N(2))<br>
* The size of the list must be equal to Σ N(d) = the sum of the number of elements per dimension N(d) over all dimensions D.
* To convert the raw axis values to physical values, the conversion rule of the respective dimension must be applied
* (CCBLOCK referenced by ca_cc_axis_conversion[i]). If the CCBLOCK link of a dimension is NIL,
* the raw values are equal to the physical values (1:1 conversion).
* </p>
*/
private double[] axisValues;
/**
* List of cycle counts for each element in case of "CG/DG template" storage.
* <p>Only present for storage types "CG/DG template"!</p>
* <p>
* List of cycle counts for each element in case of "CG/DG template" storage. The offsets are stored line-oriented,
* i.e. element k uses ca_cycle_count[k]<br>
* The size of the list must be equal to Π N(d) = the product of the number of elements per dimension N(d) over all dimensions D.<br>
* Note: ca_cycle_count[0] must be equal to cg_cycle_count of parent CGBLOCK!
* </p>
*/
private long[] cycleCounts;
private CABLOCK(SeekableByteChannel sbc, long pos)
{
super(sbc, pos);
}
/**
* Reads a CABLOCK from the channel starting at current channel position.
*
* @param channel
* The channel to read from.
* @param pos
* The position
* @return The block data.
* @throws IOException
* The exception.
*/
public static CABLOCK read(SeekableByteChannel channel, long pos) throws IOException {
CABLOCK block = new CABLOCK(channel, pos);
// read block header
ByteBuffer bb = ByteBuffer.allocate(24);
bb.order(ByteOrder.LITTLE_ENDIAN);
channel.position(pos);
channel.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));
// read block data
bb = ByteBuffer.allocate((int) block.getLength() - 24);
bb.order(ByteOrder.LITTLE_ENDIAN);
channel.position(pos + 24);
channel.read(bb);
bb.rewind();
// read links
long[] lnks = new long[(int) block.getLinkCount()];
for (int i = 0; i < lnks.length; i++) {
lnks[i] = MDF4Util.readLink(bb);
}
// read data
// UINT8: Array type (ca_type)
byte arrayType = MDF4Util.readUInt8(bb);
block.setArrayType(arrayType);
// UINT8: Storage type (ca_storage)
byte storageTyp = MDF4Util.readUInt8(bb);
block.setStorageType(storageTyp);
// UINT16: Number of dimensions (ca_ndim)
int nrOfDims = MDF4Util.readUInt16(bb);
block.setNrOfDimensions(nrOfDims);
// UINT32: Flags (ca_flags)
long readFlags = MDF4Util.readUInt32(bb);
block.setFlags(readFlags);
// INT32: Byte offset base (ca_byte_offset_base)
block.setByteOffsetBase(MDF4Util.readInt32(bb));
// UINT32: Invalidation bit position base (ca_inval_bit_pos_base)
block.setInvalidationBitPositionBase(MDF4Util.readUInt32(bb));
// UINT64: Maximum size of each dimension (ca_dim_size)
List<Long> dimSizesList = new ArrayList<>();
for (int i = 0; i < nrOfDims; i++) {
dimSizesList.add(MDF4Util.readUInt64(bb));
}
block.setDimSizes(dimSizesList);
long sumNumberOfValuesPerDimension = dimSizesList.stream().mapToLong(Long::longValue).sum();
long productNumberOfValuesPerDimension = dimSizesList.stream().mapToLong(Long::longValue).reduce(1, (a, b) -> a * b);
// REAL: Axis value for each dimension's number of values (ca_axis_value)
if (isBitSet(readFlags, FLAG_FIXED_AXES)) {
List<Double> axisValuesList = new ArrayList<>();
for (long i = 0; i < sumNumberOfValuesPerDimension; i++) {
axisValuesList.add(MDF4Util.readReal(bb));
}
block.setAxisValues(axisValuesList);
}
// UINT64: Cycle counts (ca_cycle_count)
if (isBitSet(storageTyp, STORAGETYPE_CG_TEMPLATE) || isBitSet(storageTyp, STORAGETYPE_DG_TEMPLATE)) {
List<Long> cycleCountsList = new ArrayList<>();
for (long i = 0; i < productNumberOfValuesPerDimension; i++) {
cycleCountsList.add(MDF4Util.readUInt64(bb));
}
block.setCycleCounts(cycleCountsList);
}
// extract links after reading data
int currentLinkIndex = 0;
// ca_composition
long lnkNestedComposition = lnks[currentLinkIndex++];
block.setLnkComposition(lnkNestedComposition);
// ca_data
if (isBitSet(storageTyp, STORAGETYPE_DG_TEMPLATE)) {
List<Long> lnkDataList = new ArrayList<>();
for (long i = 0; i < productNumberOfValuesPerDimension; i++) {
lnkDataList.add(lnks[currentLinkIndex++]);
}
block.setLnksData(lnkDataList);
}
// ca_dynamic_size
if (isBitSet(readFlags, FLAG_DYNAMIC_SIZE)) {
List<Long> lnkDynamicSizeList = new ArrayList<>();
for (long i = 0; i < nrOfDims * 3; i++) {
lnkDynamicSizeList.add(lnks[currentLinkIndex++]);
}
block.setLnksDynamicSize(lnkDynamicSizeList);
}
// ca_input_quantity
if (isBitSet(readFlags, FLAG_INPUT_QUANTITY_DEFINED)) {
List<Long> lnkInputQuantityList = new ArrayList<>();
for (long i = 0; i < nrOfDims * 3; i++) {
lnkInputQuantityList.add(lnks[currentLinkIndex++]);
}
block.setLnksInputQuantities(lnkInputQuantityList);
}
// ca_output_quantity
if (isBitSet(readFlags, FLAG_OUTPUT_QUANTITY_DEFINED)) {
List<Long> lnkOutputQuantityList = new ArrayList<>();
for (long i = 0; i < 3; i++) {
lnkOutputQuantityList.add(lnks[currentLinkIndex++]);
}
block.setLnksOutputQuantity(lnkOutputQuantityList);
}
// ca_comparison_quantity
if (isBitSet(readFlags, FLAG_COMPARISON_QUANTITY_DEFINED)) {
List<Long> lnkComparisonQuantityList = new ArrayList<>();
for (long i = 0; i < 3; i++) {
lnkComparisonQuantityList.add(lnks[currentLinkIndex++]);
}
block.setLnksComparisonQuantity(lnkComparisonQuantityList);
}
// ca_cc_axis_conversion
if (isBitSet(readFlags, FLAG_SCALING_AXIS_DEFINED)) {
List<Long> lnkConversionRuleList = new ArrayList<>();
for (long i = 0; i < nrOfDims; i++) {
lnkConversionRuleList.add(lnks[currentLinkIndex++]);
}
block.setLnksConversionRule(lnkConversionRuleList);
}
// ca_axis
if (isBitSet(readFlags, FLAG_SCALING_AXIS_DEFINED) && !isBitSet(readFlags, FLAG_FIXED_AXES)) {
List<Long> lnkScalingAxisList = new ArrayList<>();
for (long i = 0; i < nrOfDims * 3; i++) {
lnkScalingAxisList.add(lnks[currentLinkIndex++]);
}
block.setLnksScalingAxis(lnkScalingAxisList);
}
// throw exception if found unsupported features
if (lnkNestedComposition > 0) {
throw new IOException("Nested composition is not yet supported! Found at CABlock: [" + block + "]");
}
if (isBitSet(arrayType, ARRAYTYPE_INTERVAL_AXIS)) {
throw new IOException("Array type 'Interval Axis' is not yet supported! Found at CABlock: [" + block + "]");
}
else if (isBitSet(arrayType, ARRAYTYPE_CLASSIFICATION_RESULT)) {
throw new IOException("Array type 'Classification Result' is not yet supported! Found at CABlock: [" + block + "]");
}
if (isBitSet(storageTyp, STORAGETYPE_CG_TEMPLATE)) {
throw new IOException("Storage type 'CG Template' is not yet supported! Found at CABlock: [" + block + "]");
}
else if (isBitSet(storageTyp, STORAGETYPE_DG_TEMPLATE)) {
throw new IOException("Storage type 'DG Template' is not yet supported! Found at CABlock: [" + block + "]");
}
return block;
}
private void setArrayType(byte arrayType) {
this.arrayType = arrayType;
}
private void setStorageType(byte storageType) {
this.storageType = storageType;
}
private void setNrOfDimensions(int nrOfDimensions) {
this.nrOfDimensions = nrOfDimensions;
}
private void setFlags(long flags) {
this.flags = flags;
}
private void setByteOffsetBase(long byteOffsetBase) {
this.byteOffsetBase = byteOffsetBase;
}
private void setInvalidationBitPositionBase(long invalBitPosBase) {
this.invalBitPosBase = invalBitPosBase;
}
private void setDimSizes(List<Long> dimSizesList) {
this.dimSizes = dimSizesList.stream().mapToLong(i -> i).toArray();
}
private void setAxisValues(List<Double> axisValues) {
this.axisValues = axisValues.stream().mapToDouble(i -> i).toArray();
}
private void setCycleCounts(List<Long> cycleCounts) {
this.cycleCounts = cycleCounts.stream().mapToLong(i -> i).toArray();
}
private void setLnkComposition(long lnkComposition) {
this.lnkComposition = lnkComposition;
}
private void setLnksData(List<Long> lnksData) {
this.lnksData = lnksData.stream().mapToLong(i -> i).toArray();
}
private void setLnksDynamicSize(List<Long> lnksDynamicSize) {
this.lnksDynamicSize = lnksDynamicSize.stream().mapToLong(i -> i).toArray();
}
private void setLnksInputQuantities(List<Long> lnksInputQuantities) {
this.lnksInputQuantity = lnksInputQuantities.stream().mapToLong(i -> i).toArray();
}
private void setLnksOutputQuantity(List<Long> lnksOutputQuantity) {
this.lnksOutputQuantity = lnksOutputQuantity.stream().mapToLong(i -> i).toArray();
}
private void setLnksComparisonQuantity(List<Long> lnksComparisonQuantity) {
this.lnksComparisonQuantity = lnksComparisonQuantity.stream().mapToLong(i -> i).toArray();
}
private void setLnksConversionRule(List<Long> lnksConversionRule) {
this.lnksConversionRule = lnksConversionRule.stream().mapToLong(i -> i).toArray();
}
private void setLnksScalingAxis(List<Long> lnksScalingAxis) {
this.lnksScalingAxis = lnksScalingAxis.stream().mapToLong(i -> i).toArray();
}
/**
* @return the arrayType
*/
public byte getArrayType()
{
return arrayType;
}
/**
* @return the storageType
*/
public byte getStorageType()
{
return storageType;
}
/**
* @return the flags
*/
public long getFlags()
{
return flags;
}
/**
* @return the byteOffsetBase
*/
public long getByteOffsetBase()
{
return byteOffsetBase;
}
/**
* @return the invalBitPosBase
*/
public long getInvalBitPosBase()
{
return invalBitPosBase;
}
/**
* @return the dimSize for given dimension index
*/
public long getDimSize(int dimIndex)
{
return dimSizes[dimIndex];
}
/**
* @return all value sizes of dimensions
*/
public long[] getDimSizes()
{
return dimSizes;
}
/**
* @return the nrOfDimensions
*/
public int getNrOfDimensions()
{
return nrOfDimensions;
}
/**
* @return the axisValues
*/
public double[] getAxisValues()
{
return axisValues;
}
/**
* @return the cycleCounts
*/
public long[] getCycleCounts()
{
return cycleCounts;
}
public BLOCK getCompositionBlock() throws IOException
{
if (lnkComposition > 0) {
String blockType = getBlockType(sbc, lnkComposition);
BLOCK compositionBlock = null;
// link points to a CNBLOCK
if (blockType.equals(CNBLOCK.BLOCK_ID)) {
compositionBlock = CNBLOCK.read(sbc, lnkComposition);
}
// link points to CABLOCK
else if (blockType.equals(CABLOCK.BLOCK_ID)) {
compositionBlock = CABLOCK.read(sbc, lnkComposition);
}
// unknown
else {
throw new IOException("Unsupported block type for Composition: " + blockType);
}
throw new IOException("Composition of CABLOCKs is not yet supported! Found the following block: " + compositionBlock);
}
return null;
}
/**
* Checks if the given bitConstant is set in the given bitfield.
*
* @param bits
* @param bitConstant
* @return
*/
private static boolean isBitSet(long bits, byte bitConstant) {
return (bits & bitConstant) != 0;
}
/**
* Returns the requested flag value.
*
* @param flagConstant one of the flag constants given in this class
* @return
*/
public boolean isFlagSet(byte flagConstant) {
return (flags & flagConstant) != 0;
}
/**
* This method should only be called if the flag FLAG_SCALING_AXIS_DEFINED is
* set. If this is true and null is returned here, it
*
* @param dimIndex
* @return
*/
public long getConversionRuleOffsetForDimension(int dimIndex)
{
if (lnksConversionRule != null && dimIndex < nrOfDimensions && lnksConversionRule.length == 3 * nrOfDimensions)
{
return lnksConversionRule[nrOfDimensions * 3 - 1];
}
return -1;
}
/**
* This method should only be called if the flag FLAG_SCALING_AXIS_DEFINED is
* set and FLAG_FIXED_AXIS is not set.
*
* @param dimIndex
* the index of the dimension to get the scaling axis offsets for
* @return a {@link BlockReferenceTriple} containing the offsets to read the
* CNBLOCK defining the scaling axis for the dimension with the
* given index or null if none defined
*/
public BlockReferenceTriple getScalingAxisOffsetsForDimension(int dimIndex)
{
return createReferenceTriple(dimIndex, lnksScalingAxis);
}
/**
* This method should only be called if the flag FLAG_INPUT_QUANTITY_DEFINED
* is set.
*
* @param dimIndex
* the index of the dimension to get the input quantity offsets for
* @return a {@link BlockReferenceTriple} containing the offsets to read the
* CNBLOCK defining the input quantity for the dimension with the
* given index or null if none defined
*/
public BlockReferenceTriple getInputQuantityOffsetsForDimension(int dimIndex)
{
return createReferenceTriple(dimIndex, lnksInputQuantity);
}
/**
* This method should only be called if the flag FLAG_OUTPUR_QUANTITY_DEFINED
* is set.
*
* @param dimIndex
* the index of the dimension to get the output quantity offsets for
* @return a {@link BlockReferenceTriple} containing the offsets to read the
* CNBLOCK defining the output quantity for the dimension with the
* given index or null if none defined
*/
public BlockReferenceTriple getOutputQuantityOffsetsForDimension(int dimIndex)
{
return createReferenceTriple(dimIndex, lnksOutputQuantity);
}
/**
* This method should only be called if the flag
* FLAG_COMPARISON_QUANTITY_DEFINED is set.
*
* @param dimIndex
* the index of the dimension to get the comparison quantity offsets
* for
* @return a {@link BlockReferenceTriple} containing the offsets to read the
* CNBLOCK defining the comparison quantity for the dimension with the
* given index or null if none defined
*/
public BlockReferenceTriple getComparisonQuantityOffsetsForDimension(int dimIndex)
{
return createReferenceTriple(dimIndex, lnksComparisonQuantity);
}
/**
* This method should only be called if the flag FLAG_DYNAMIC_SIZE is set.
*
* @param dimIndex
* the index of the dimension to get the dynamic size signal offsets
* for
* @return a {@link BlockReferenceTriple} containing the offsets to read the
* CNBLOCK defining the dynamic size signal for the dimension with the
* given index or null if none defined
*/
public BlockReferenceTriple getSizeSignalOffsetsForDimension(int dimIndex)
{
return createReferenceTriple(dimIndex, lnksDynamicSize);
}
/**
* Creates a {@link BlockReferenceTriple} from the given offsets for the given
* dimension index.
*
* @param dimIndex
* the index of the dimension to get the offset triple for
* @param offsets
* the offset structure to read from
* @return a {@link BlockReferenceTriple} containing the requested offsets or
* null if none defined
*/
private BlockReferenceTriple createReferenceTriple(int dimIndex, long[] offsets)
{
if (offsets != null && dimIndex < nrOfDimensions && offsets.length == 3 * nrOfDimensions)
{
return new BlockReferenceTriple(offsets[dimIndex * 3 + 0], offsets[dimIndex * 3 + 1], offsets[dimIndex * 3 + 2]);
}
return null;
}
/**
* @see java.lang.Object#toString()
*
* @return
*/
@Override
public String toString()
{
return "CABLOCK [lnkComposition=" + lnkComposition + ", lnksData=" + Arrays.toString(lnksData)
+ ", lnksDynamicSize=" + Arrays.toString(lnksDynamicSize) + ", lnksInputQuantities="
+ Arrays.toString(lnksInputQuantity) + ", lnksOutputQuantity=" + Arrays.toString(lnksOutputQuantity)
+ ", lnksComparisonQuantity=" + Arrays.toString(lnksComparisonQuantity) + ", lnksScalingAxis="
+ Arrays.toString(lnksConversionRule) + ", lnksAxis=" + Arrays.toString(lnksScalingAxis) + ", arrayType=" + arrayType
+ ", storageType=" + storageType + ", flags=" + flags + ", byteOffsetBase=" + byteOffsetBase
+ ", invalBitPosBase=" + invalBitPosBase + ", dimSizes=" + Arrays.toString(dimSizes) + ", nrOfDimensions="
+ nrOfDimensions + ", axisValues=" + Arrays.toString(axisValues) + ", cycleCounts="
+ Arrays.toString(cycleCounts) + "]";
}
}