package org.eclipse.mdm.openatfx.mdf.mdf4; | |
import java.io.IOException; | |
import java.nio.Buffer; | |
import java.nio.ByteBuffer; | |
import java.nio.ByteOrder; | |
import java.nio.channels.SeekableByteChannel; | |
import java.nio.file.Files; | |
import java.nio.file.Paths; | |
import java.nio.file.StandardOpenOption; | |
import java.util.ArrayList; | |
import java.util.List; | |
import java.util.stream.Collectors; | |
import org.apache.commons.logging.Log; | |
import org.apache.commons.logging.LogFactory; | |
public class ChannelReader | |
{ | |
private static final Log LOG = LogFactory.getLog(ChannelReader.class); | |
private static final int MAX_BUFFER_SIZE = 2048; | |
private final DGBLOCK dgBlock; | |
private final CGBLOCK cgBlock; | |
private final CNBLOCK cnBlock; | |
public ChannelReader(String mdfPath, long dgOffset, long cgOffset, long cnOffset) | |
{ | |
SeekableByteChannel sbc; | |
try | |
{ | |
sbc = Files.newByteChannel(Paths.get(mdfPath), StandardOpenOption.READ); | |
dgBlock = DGBLOCK.read(sbc, dgOffset); | |
cgBlock = CGBLOCK.read(sbc, cgOffset); | |
cnBlock = CNBLOCK.read(sbc, cnOffset); | |
} | |
catch (IOException e) | |
{ | |
throw new RuntimeException("Error reading from mdf file: " + e.getLocalizedMessage()); | |
} | |
} | |
public static void main(String[] args) | |
{ | |
if (args.length != 4) | |
{ | |
LOG.error("Wrong arguments: Must be called with the mdf filepath and the offsets for DGBLOCK, CGBLOCK and CNBLOCK!"); | |
} | |
else | |
{ | |
String mdfPath = null; | |
long dgOffset = -1; | |
long cgOffset = -1; | |
long cnOffset = -1; | |
for (int i = 0; i < 4; i++) | |
{ | |
String arg = args[i]; | |
switch (i) | |
{ | |
case 0: | |
mdfPath = arg.trim(); | |
break; | |
case 1: | |
dgOffset = Long.valueOf(arg); | |
break; | |
case 2: | |
cgOffset = Long.valueOf(arg); | |
break; | |
case 3: | |
cnOffset = Long.valueOf(arg); | |
break; | |
default: | |
break; | |
} | |
} | |
ChannelReader reader = new ChannelReader(mdfPath, dgOffset, cgOffset, cnOffset); | |
try | |
{ | |
LOG.info(reader.readValues()); | |
} | |
catch (IOException e) | |
{ | |
LOG.error("Error reading channel values: " + e.getLocalizedMessage()); | |
} | |
} | |
} | |
public String readValues() throws IOException | |
{ | |
DTBLOCK dataBlock = DTBLOCK.read(dgBlock.sbc, dgBlock.getLnkData()); | |
long startDataOffset = dgBlock.getLnkData() + 24; // header of DTBLOCK is 24 bytes long | |
long dataBlockLength = dataBlock.getLength(); | |
long position = dgBlock.getLnkData() + 24 + cnBlock.getByteOffset(); | |
switch (cnBlock.getDataType()) | |
{ | |
case 0: | |
List<Long> values0 = readIntsFromByteChannel(true, true, (int)cnBlock.getBitCount(), cnBlock.sbc, position, (int)cgBlock.getCycleCount()); | |
return values0.stream().map(String::valueOf).collect(Collectors.joining(", ")); | |
case 1: | |
List<Long> values1 = readIntsFromByteChannel(true, false, (int)cnBlock.getBitCount(), cnBlock.sbc, position, (int)cgBlock.getCycleCount()); | |
return values1.stream().map(String::valueOf).collect(Collectors.joining(", ")); | |
case 2: | |
List<Long> values2 = readIntsFromByteChannel(false, true, (int)cnBlock.getBitCount(), cnBlock.sbc, position, (int)cgBlock.getCycleCount()); | |
return values2.stream().map(String::valueOf).collect(Collectors.joining(", ")); | |
case 3: | |
List<Long> values3 = readIntsFromByteChannel(false, false, (int)cnBlock.getBitCount(), cnBlock.sbc, position, (int)cgBlock.getCycleCount()); | |
return values3.stream().map(String::valueOf).collect(Collectors.joining(", ")); | |
case 4: | |
List<Double> values4 = readRealsFromByteChannel(cnBlock.sbc, true, position, (int)cgBlock.getCycleCount()); | |
return values4.stream().map(String::valueOf).collect(Collectors.joining(", ")); | |
case 5: | |
List<Double> values5 = readRealsFromByteChannel(cnBlock.sbc, false, position, (int)cgBlock.getCycleCount()); | |
return values5.stream().map(String::valueOf).collect(Collectors.joining(", ")); | |
default: | |
throw new RuntimeException("readValues() not supported yet for String or complex datatypes!"); | |
} | |
} | |
public List<Long> readIntsFromByteChannel(boolean unsigned, boolean littleEndian, int bits, SeekableByteChannel channel, long pos, int readCount) throws IOException | |
{ | |
ByteBuffer bb = ByteBuffer.allocate(bits / 8 * readCount); | |
if (littleEndian) | |
{ | |
bb.order(ByteOrder.LITTLE_ENDIAN); | |
} | |
else | |
{ | |
bb.order(ByteOrder.BIG_ENDIAN); | |
} | |
channel.position(pos); | |
channel.read(bb); | |
bb.rewind(); | |
List<Long> readValues = new ArrayList<>(); | |
for (int i = 0; i < readCount; i++) | |
{ | |
switch (bits) | |
{ | |
case 8: | |
readValues.add((long) MDF4Util.readUInt8(bb)); | |
break; | |
case 16: | |
if (unsigned) | |
{ | |
readValues.add((long) MDF4Util.readUInt16(bb)); | |
} | |
else | |
{ | |
readValues.add((long) MDF4Util.readInt16(bb)); | |
} | |
break; | |
case 32: | |
if (unsigned) | |
{ | |
readValues.add(MDF4Util.readUInt32(bb)); | |
} | |
else | |
{ | |
readValues.add((long) MDF4Util.readInt32(bb)); | |
} | |
break; | |
case 64: | |
if (unsigned) | |
{ | |
readValues.add(MDF4Util.readUInt64(bb)); | |
} | |
else | |
{ | |
readValues.add(MDF4Util.readInt64(bb)); | |
} | |
break; | |
default: | |
break; | |
} | |
} | |
return readValues; | |
} | |
public List<Double> readRealsFromByteChannel(SeekableByteChannel channel, boolean littleEndian, long pos, int readCount) throws IOException | |
{ | |
ByteBuffer bb = ByteBuffer.allocate(8 * readCount); | |
if (littleEndian) | |
{ | |
bb.order(ByteOrder.LITTLE_ENDIAN); | |
} | |
else | |
{ | |
bb.order(ByteOrder.BIG_ENDIAN); | |
} | |
channel.position(pos); | |
channel.read(bb); | |
bb.rewind(); | |
List<Double> readValues = new ArrayList<>(); | |
for (int i = 0; i < readCount; i++) | |
{ | |
readValues.add(MDF4Util.readReal(bb)); | |
} | |
return readValues; | |
} | |
public void temp(short bitCount, short bitOffset, ByteBuffer sourceMbb) | |
{ | |
// read that number of bytes from the byte position within the file | |
int bytesToRead = ((bitCount + bitOffset - 1) / 8) + 1; | |
byte[] tmp = new byte[bytesToRead]; | |
sourceMbb.get(tmp); | |
// put the byte into an integer to enable bit shifting | |
if (tmp.length <= 4) { | |
ByteBuffer bb = ByteBuffer.allocate(4); | |
bb.order(sourceMbb.order()); | |
bb.put(tmp); | |
Buffer.class.cast(bb).rewind(); // workaround: make buildable with both java8 and java9 | |
int intValue = bb.getInt(); | |
intValue = intValue >> bitOffset; // shift right bit offset | |
int mask = 0xFFFFFFFF >>> (32 - bitCount); // mask out unnecessary bits | |
intValue = intValue & mask; | |
// list.add(intValue); | |
} | |
// put the byte into a long to enable bit shifting | |
else { | |
ByteBuffer bb = ByteBuffer.allocate(8); | |
bb.order(sourceMbb.order()); | |
bb.put(tmp); | |
Buffer.class.cast(bb).rewind(); // workaround: make buildable with both java8 and java9 | |
long longValue = bb.getLong(); | |
longValue = longValue >> bitOffset; // shift right bit offset | |
long mask = 0xFFFFFFFFFFFFFFFFl >>> (64 - bitCount); // mask out unnecessary bits | |
longValue = longValue & mask; | |
// list.add(longValue); | |
} | |
} | |
} |