blob: 56c66e2d1568564cb479021e48d4f4d8f5fbf6cb [file] [log] [blame]
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);
}
}
}