package org.yamcs.xtceproc; import java.nio.ByteBuffer; import java.nio.ByteOrder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.yamcs.parameter.Value; import org.yamcs.protobuf.Yamcs.Value.Type; import org.yamcs.utils.StringConverter; import org.yamcs.xtce.BinaryDataEncoding; import org.yamcs.xtce.DataEncoding; import org.yamcs.xtce.FloatDataEncoding; import org.yamcs.xtce.IntegerDataEncoding; import org.yamcs.xtce.IntegerDataEncoding.Encoding; import org.yamcs.xtce.StringDataEncoding; /** * Encodes TC data according to the DataEncoding definition * @author nm * */ public class DataEncodingEncoder { TcProcessingContext pcontext; Logger log=LoggerFactory.getLogger(this.getClass().getName()); public DataEncodingEncoder(TcProcessingContext pcontext) { this.pcontext = pcontext; } /** * Encode the raw value of the argument into the packet. */ public void encodeRaw(DataEncoding de, Value rawValue) { if(de instanceof IntegerDataEncoding) { encodeRawInteger((IntegerDataEncoding) de, rawValue); } else if(de instanceof FloatDataEncoding) { encodeRawFloat((FloatDataEncoding) de, rawValue); } else if(de instanceof StringDataEncoding) { encodeRawString((StringDataEncoding) de, rawValue); } else if(de instanceof BinaryDataEncoding) { encodeRawBinary((BinaryDataEncoding) de, rawValue); } else { log.error("DataEncoding {} not implemented", de); throw new IllegalArgumentException("DataEncoding "+de+" not implemented"); } } private void encodeRawInteger(IntegerDataEncoding ide, Value rawValue) { // Integer encoded as string, don't even try reading it as int if(ide.getEncoding() == Encoding.string) { encodeRaw(ide.getStringEncoding(), rawValue); return; } //STEP 0 convert the value to a long long v; switch (rawValue.getType()) { case SINT32: v = rawValue.getSint32Value(); break; case SINT64: v = rawValue.getSint64Value(); break; case UINT32: v = rawValue.getUint32Value() &0xFFFFFFFFL; break; case UINT64: v = rawValue.getUint64Value(); break; case BOOLEAN: v = rawValue.getBooleanValue()?1:0; break; case DOUBLE: v = (long) rawValue.getDoubleValue(); break; case FLOAT: v = (long) rawValue.getFloatValue(); break; default: throw new IllegalArgumentException("Cannot encode values of types " + rawValue.getType() + " to string"); } //STEP 1 extract 8 bytes from the buffer into the long x. // The first extracted byte is where the first bit of v should fit in //NOTE: in order to do this for the last arguments, the byte buffer has to be longer than the packet int byteOffset = (pcontext.bitPosition)/8; int bitOffsetInsideMask = pcontext.bitPosition-8*byteOffset; int bitsToShift = 64 - bitOffsetInsideMask - ide.getSizeInBits(); long mask = ~((-1L<<(64-ide.getSizeInBits()))>>>(64-ide.getSizeInBits()-bitsToShift)); long x = pcontext.bb.getLong(byteOffset); pcontext.bitPosition+=ide.getSizeInBits(); //STEP 2 mix the extracted bytes x with he value of the argument v, depending on the encoding type x = x & mask; switch(ide.getEncoding()) { case twosComplement: v = (v<<(64-ide.getSizeInBits())>>>(64-ide.getSizeInBits())); break; case unsigned: break; case signMagnitude: if(v<0) { v = -v; v &= (1<<bitsToShift); } break; default: throw new UnsupportedOperationException("encoding "+ide.getEncoding()+" not implemented"); } if(ide.getByteOrder()==ByteOrder.LITTLE_ENDIAN) { v = toLittleEndian(v, ide.getSizeInBits()); } x |= (v<<bitsToShift); //STEP 3 put back the extracted bytes into the buffer pcontext.bb.order(ByteOrder.BIG_ENDIAN); pcontext.bb.putLong(byteOffset, x); } private long toLittleEndian(long v, int sizeInBits) { ByteBuffer bb = ByteBuffer.allocate(8); bb.order(ByteOrder.LITTLE_ENDIAN); bb.putLong(v); bb.order(ByteOrder.BIG_ENDIAN); long lev = bb.getLong(0); lev = lev>>(64-8*(((sizeInBits-1)/8)+1)); return lev; } private void encodeRawString(StringDataEncoding sde, Value rawValue) { String v = ""; switch(rawValue.getType()) { case DOUBLE: v = rawValue.getDoubleValue() + ""; break; case FLOAT: v = rawValue.getFloatValue() + ""; break; case UINT32: v = rawValue.getUint32Value() + ""; break; case SINT32: v = rawValue.getSint32Value() + ""; break; case UINT64: v = rawValue.getUint64Value() + ""; break; case SINT64: v = rawValue.getSint64Value() + ""; break; case STRING: v = rawValue.getStringValue(); break; case BOOLEAN: v = rawValue.getBooleanValue() + ""; break; case TIMESTAMP: v = rawValue.getTimestampValue() + ""; break; case BINARY: v = StringConverter.arrayToHexString(rawValue.getBinaryValue()); break; default: throw new IllegalArgumentException("String encoding for data of type "+rawValue.getType()+" not supported"); } if(pcontext.bitPosition%8!=0) { throw new IllegalStateException("String Parameter that does not start at byte boundary not supported. bitPosition:"+pcontext.bitPosition); } byte[] rawValueBytes = v.getBytes(); //TBD encoding int initialBitPosition = pcontext.bitPosition; switch(sde.getSizeType()) { case Fixed: int byteOffset = pcontext.bitPosition/8; int sdeSizeInBytes = sde.getSizeInBits()/8; int sizeInBytes = (sdeSizeInBytes > rawValueBytes.length)?rawValueBytes.length:sdeSizeInBytes; pcontext.bb.position(byteOffset); pcontext.bb.put(rawValueBytes, 0, sizeInBytes); if(sdeSizeInBytes>rawValueBytes.length) { //fill up with nulls to reach the required size byte[] nulls = new byte[sdeSizeInBytes - sizeInBytes]; pcontext.bb.put(nulls); } pcontext.bitPosition+=sde.getSizeInBits(); break; case LeadingSize: pcontext.bb.order(ByteOrder.BIG_ENDIAN); //TBD byteOffset = pcontext.bitPosition/8; switch(sde.getSizeInBitsOfSizeTag()) { case 8: pcontext.bb.put(byteOffset, (byte) rawValueBytes.length); pcontext.bitPosition+=8; break; case 16: pcontext.bb.putShort(byteOffset, (short) rawValueBytes.length); pcontext.bitPosition+=16; break; case 32: pcontext.bb.putInt(byteOffset, (short) rawValueBytes.length); pcontext.bitPosition+=32; break; default: throw new IllegalArgumentException("SizeInBits of Size Tag of "+sde.getSizeInBitsOfSizeTag()+" not supported"); } int rvByteOffset = pcontext.bitPosition/8; pcontext.bb.position(rvByteOffset); pcontext.bb.put(rawValueBytes, 0, rawValueBytes.length); pcontext.bitPosition = pcontext.bb.position() * 8; break; case TerminationChar: byteOffset=pcontext.bitPosition/8; pcontext.bb.position(byteOffset); pcontext.bb.put(rawValueBytes, 0, rawValueBytes.length); pcontext.bb.put(sde.getTerminationChar()); pcontext.bitPosition = pcontext.bb.position() * 8; break; } int newBitPosition = pcontext.bitPosition; if((sde.getSizeInBits()!=-1) && (newBitPosition-initialBitPosition<sde.getSizeInBits())) { pcontext.bitPosition = initialBitPosition+sde.getSizeInBits(); } } private void encodeRawFloat(FloatDataEncoding de, Value rawValue) { if(pcontext.bitPosition%8!=0) { log.warn("Float Parameter that does not start at byte boundary not supported. bitPosition: {}", pcontext.bitPosition); } switch(de.getEncoding()) { case IEEE754_1985: encodeRawIEEE754_1985(de, rawValue); break; case STRING: encodeRaw(de.getStringDataEncoding(), rawValue); break; default: throw new IllegalArgumentException("Float Encoding "+de.getEncoding()+" not implemented"); } } private void encodeRawIEEE754_1985(FloatDataEncoding de, Value rawValue) { if(pcontext.bitPosition%8!=0) { throw new IllegalArgumentException("Float Argument that does not start at byte boundary not supported. bitPosition: "+pcontext.bitPosition); } double v; switch(rawValue.getType()) { case DOUBLE: v = rawValue.getDoubleValue(); break; case FLOAT: v = rawValue.getFloatValue(); break; case UINT32: v = rawValue.getUint32Value(); break; case SINT32: v = rawValue.getSint32Value(); break; case UINT64: v = rawValue.getUint64Value(); break; case SINT64: v = rawValue.getSint64Value(); break; default: throw new IllegalArgumentException("Float encoding for data of type "+rawValue.getType()+" not supported"); } pcontext.bb.order(de.getByteOrder()); int byteOffset=pcontext.bitPosition/8; if(de.getSizeInBits()==32) { pcontext.bb.putFloat(byteOffset, (float)v); } else { pcontext.bb.putDouble(byteOffset, v); } pcontext.bb.order(ByteOrder.BIG_ENDIAN); pcontext.bitPosition+=de.getSizeInBits(); } private void encodeRawBinary(BinaryDataEncoding bde, Value rawValue) { if((pcontext.bitPosition&7)!=0) { throw new IllegalStateException("Binary Parameter that does not start at byte boundary not supported. bitPosition:"+pcontext.bitPosition); } byte[] v; if(rawValue.getType()==Type.BINARY) { v = rawValue.getBinaryValue(); } else if (rawValue.getType() == Type.STRING) { v = rawValue.getStringValue().getBytes(); //TBD encoding } else { throw new IllegalArgumentException("Cannot encode as binary data values of type "+rawValue.getType()); } int sizeInBytes = bde.getSizeInBits()/8; if(sizeInBytes>v.length) { sizeInBytes = v.length; } pcontext.bb.position(pcontext.bitPosition/8); pcontext.bb.put(v, 0, sizeInBytes); pcontext.bitPosition+=bde.getSizeInBits(); } }