/*******************************************************************************
* This file is protected by Copyright.
* Please refer to the COPYRIGHT file distributed with this source distribution.
*
* This file is part of REDHAWK IDE.
*
* All rights reserved. This program and the accompanying materials are made available under
* the terms of the Eclipse Public License v1.0 which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package gov.redhawk.ide.snapshot.writer.internal;
import gov.redhawk.bulkio.util.BulkIOType;
import gov.redhawk.ide.snapshot.internal.ui.SnapshotMetaData.CFDataType;
import gov.redhawk.ide.snapshot.internal.ui.SnapshotMetaData.SnapshotMetadataFactory;
import gov.redhawk.ide.snapshot.internal.ui.SnapshotMetaData.Value;
import gov.redhawk.ide.snapshot.writer.BaseDataWriter;
import gov.redhawk.ide.snapshot.writer.IDataWriterSettings;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.LongBuffer;
import java.nio.ShortBuffer;
import java.nio.channels.FileChannel;
import mil.jpeojtrs.sca.util.AnyUtils;
import mil.jpeojtrs.sca.util.UnsignedUtils;
import BULKIO.PrecisionUTCTime;
import CF.DataType;
import CF.DataTypeHelper;
/**
* NOTE1: the BulkIO data type MUST NOT be changing during a Port snapshot.
* NOTE2: this class is NOT meant to have pushPacket(..) calls from different threads simultaneously,
* that should never happen in normal operation.
*/
public abstract class BinDataWriter extends BaseDataWriter {
private RandomAccessFile raf;
private FileChannel fileChannel;
private ByteBuffer byteBuffer;
private ByteOrder byteOrder;
private long numSamples;
/**
* This method collects the information in the CF.DataType and stores it
* in the EMF model CFDataType
* @param cfType : the EMF model for the CF.DataType
* @param data :a CF.DataType to be processed
* @return the EMF model containing the data from the CF.DataType
*/
protected CFDataType readCFDataType(CFDataType cfType, DataType data) {
Object temp;
if (DataTypeHelper.type().equivalent(data.value.type())) {
temp = DataTypeHelper.extract(data.value);
} else {
temp = AnyUtils.convertAny(data.value);
}
cfType.setId(data.id);
if (temp instanceof DataType) {
cfType.setValue(readCFDataType(SnapshotMetadataFactory.eINSTANCE.createCFDataType(), (DataType) temp));
} else {
Value value = SnapshotMetadataFactory.eINSTANCE.createValue();
value.setValue(temp.toString());
//value.setJavaType(DataReceiverUtils.getAnyTypeDataType(temp));
value.setJavaType(temp.getClass().getName());
cfType.setValue(value);
}
return cfType;
}
public long getNumSamples() {
return numSamples;
}
@Override
public void open() throws IOException {
if (isOpen()) {
return;
}
raf = new RandomAccessFile(getFileDestination(), "rw");
fileChannel = raf.getChannel();
IDataWriterSettings settings = getSettings();
if (settings instanceof BinDataWriterSettings) {
BinDataWriterSettings binSettings = (BinDataWriterSettings) settings;
byteOrder = binSettings.getByteOrder();
} else {
byteOrder = BinDataWriterSettings.DEFAULT_BYTE_ORDER;
}
setOpen(true);
}
private ByteBuffer allocateByteBuffer(int byteBufferSize) {
ByteBuffer buffer = this.byteBuffer;
if ((buffer == null) || (buffer.capacity() < byteBufferSize)) {
buffer = ByteBuffer.allocateDirect(byteBufferSize);
buffer.order(byteOrder);
this.byteBuffer = buffer;
}
buffer.position(0); // reset buffer's position
buffer.limit(byteBufferSize);
return buffer;
}
@Override
public void pushPacket(char[] data, int offset, int length, PrecisionUTCTime time) throws IOException {
ByteBuffer buffer = allocateByteBuffer(length * getSettings().getType().getBytePerAtom());
for (int i = offset; i < length; i++) {
buffer.putChar(data[i]);
}
fileChannel.write(buffer);
numSamples += length;
}
@Override
public void pushPacket(double[] data, int offset, int length, PrecisionUTCTime time) throws IOException {
ByteBuffer buffer = allocateByteBuffer(length * getSettings().getType().getBytePerAtom());
DoubleBuffer tBuff = buffer.asDoubleBuffer();
tBuff.put(data, offset, length);
fileChannel.write(buffer);
numSamples += length;
}
@Override
public void pushPacket(float[] data, int offset, int length, PrecisionUTCTime time) throws IOException {
ByteBuffer buffer = allocateByteBuffer(length * getSettings().getType().getBytePerAtom());
FloatBuffer tBuff = buffer.asFloatBuffer();
tBuff.put(data, offset, length);
fileChannel.write(buffer);
numSamples += length;
}
@Override
public void pushPacket(long[] data, int offset, int length, PrecisionUTCTime time) throws IOException {
ByteBuffer buffer;
boolean upcastUnsignedType = getSettings().isUpcastUnsigned() && getSettings().getType().isUnsigned();
if (upcastUnsignedType) {
throw new IOException("Can not store ulong long as upcasted value.");
// TODO -should we still have to signed 64-bit integer and cap upper value like in corbareceiver?
} else {
buffer = allocateByteBuffer(length * getSettings().getType().getBytePerAtom());
LongBuffer tBuff = buffer.asLongBuffer();
tBuff.put(data, offset, length);
}
fileChannel.write(buffer);
numSamples += length;
}
@Override
public void pushPacket(int[] data, int offset, int length, PrecisionUTCTime time) throws IOException {
ByteBuffer buffer;
boolean upcastUnsignedType = getSettings().isUpcastUnsigned() && getSettings().getType().isUnsigned();
if (upcastUnsignedType) {
buffer = allocateByteBuffer(length * BulkIOType.LONG_LONG.getBytePerAtom());
LongBuffer tBuff = buffer.asLongBuffer();
for (int i = offset; i < length; i++) {
tBuff.put(UnsignedUtils.toSigned(data[i]));
}
} else {
buffer = allocateByteBuffer(length * getSettings().getType().getBytePerAtom());
IntBuffer tBuff = buffer.asIntBuffer();
tBuff.put(data, offset, length);
}
fileChannel.write(buffer);
numSamples += length;
}
@Override
public void pushPacket(short[] data, int offset, int length, PrecisionUTCTime time) throws IOException {
ByteBuffer buffer;
boolean upcastUnsignedType = getSettings().isUpcastUnsigned() && getSettings().getType().isUnsigned();
if (upcastUnsignedType) {
buffer = allocateByteBuffer(length * BulkIOType.LONG.getBytePerAtom());
IntBuffer tBuff = buffer.asIntBuffer();
for (int i = offset; i < length; i++) {
tBuff.put(UnsignedUtils.toSigned(data[i]));
}
} else {
buffer = allocateByteBuffer(length * getSettings().getType().getBytePerAtom());
ShortBuffer tBuf = buffer.asShortBuffer();
tBuf.put(data, offset, length);
}
fileChannel.write(buffer);
numSamples += length;
}
@Override
public void pushPacket(byte[] data, int offset, int length, PrecisionUTCTime time) throws IOException {
ByteBuffer buffer;
boolean upcastUnsignedType = getSettings().isUpcastUnsigned() && getSettings().getType().isUnsigned();
if (upcastUnsignedType) {
buffer = allocateByteBuffer(length * BulkIOType.SHORT.getBytePerAtom());
ShortBuffer tBuff = buffer.asShortBuffer();
for (int i = offset; i < length; i++) {
tBuff.put(UnsignedUtils.toSigned(data[i]));
}
} else {
buffer = ByteBuffer.wrap(data, offset, length);
}
fileChannel.write(buffer);
numSamples += length;
}
@Override
public void close() throws IOException {
setOpen(false);
if (fileChannel != null) {
saveMetaData();
// truncate file to actual size for case when we are overwriting an existing file
BulkIOType type = getSettings().getType();
long fileSizeInBytes = numSamples * type.getBytePerAtom();
fileChannel.truncate(fileSizeInBytes);
fileChannel.close();
fileChannel = null;
}
if (raf != null) {
raf.close();
raf = null;
}
numSamples = 0;
}
public File getMetaDataFile() {
File destination = getFileDestination();
int iLastDot = destination.getName().lastIndexOf('.');
if (iLastDot > 0) {
return new File(destination.getParentFile(), destination.getName().substring(0, iLastDot) + "." + getMetaDataFileExtension());
} else {
return new File(destination.getParentFile(), destination.getName() + "." + getMetaDataFileExtension());
}
}
protected ByteOrder getByteOrder() {
return byteOrder;
}
protected abstract void saveMetaData() throws IOException;
protected abstract String getMetaDataFileExtension();
}