All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.yamcs.mdb.DataEncodingEncoder Maven / Gradle / Ivy

There is a newer version: 5.10.9
Show newest version
package org.yamcs.mdb;

import static org.yamcs.mdb.DataEncodingUtils.rawRawStringValue;

import java.io.UnsupportedEncodingException;
import java.nio.ByteOrder;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yamcs.commanding.ArgumentValue;
import org.yamcs.parameter.Value;
import org.yamcs.protobuf.Yamcs.Value.Type;
import org.yamcs.utils.BitBuffer;
import org.yamcs.utils.MilStd1750A;
import org.yamcs.xtce.BinaryDataEncoding;
import org.yamcs.xtce.DataEncoding;
import org.yamcs.xtce.DynamicIntegerValue;
import org.yamcs.xtce.FloatDataEncoding;
import org.yamcs.xtce.IntegerDataEncoding;
import org.yamcs.xtce.IntegerDataEncoding.Encoding;
import org.yamcs.xtce.ParameterOrArgumentRef;
import org.yamcs.xtce.StringDataEncoding;

/**
 * Encodes TC data according to the DataEncoding definition
 */
// this class needs some cleanup: normally values arriving in the encodeRaw should match the data encoding but it's not
// always the case. We should detect where the values come from and convert them there.
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) {
        pcontext.bitbuf.setByteOrder(de.getByteOrder());

        if (de.getToBinaryTransformAlgorithm() != null) {
            DataEncoder denc = pcontext.pdata.getDataEncoder(de);
            denc.encodeRaw(de, rawValue, pcontext.bitbuf, pcontext);
        } else {
            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, pcontext);
            } 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
        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 = Math.round(rawValue.getDoubleValue());
            break;
        case FLOAT:
            v = Math.round(rawValue.getFloatValue());
            break;
        default:
            throw new IllegalArgumentException("Cannot encode values of types " + rawValue.getType() + " to integer");
        }
        int sizeInBits = ide.getSizeInBits();

        switch (ide.getEncoding()) {
        case TWOS_COMPLEMENT:
            v = (v << (64 - sizeInBits) >>> (64 - sizeInBits));
            break;
        case UNSIGNED:
            break;
        case SIGN_MAGNITUDE:
            if (v < 0) {
                v = -v;
                v &= (1 << (64 - sizeInBits));
            }
            break;
        default:
            throw new UnsupportedOperationException("encoding " + ide.getEncoding() + " not implemented");
        }
        pcontext.bitbuf.putBits(v, sizeInBits);
    }

    private void encodeRawString(StringDataEncoding sde, Value rawValue) {
        String v = rawRawStringValue(rawValue).getStringValue();
        BitBuffer bitbuf = pcontext.bitbuf;

        int initialBitPosition = bitbuf.getPosition();

        if ((initialBitPosition & 0x7) != 0) {
            throw new IllegalStateException(
                    "String Parameter that does not start at byte boundary not supported. bitPosition: "
                            + initialBitPosition);
        }

        byte[] rawValueBytes;
        try {
            rawValueBytes = v.getBytes(sde.getEncoding());
        } catch (UnsupportedEncodingException e1) {
            throw new CommandEncodingException(pcontext, "Unsupported encoding '" + sde.getEncoding() + "'");
        }

        // bmr = buffer, max, or remaining size
        int bmr = sde.getMaxSizeInBytes();
        if (bmr < 0 || bmr > bitbuf.remainingBytes()) {
            bmr = bitbuf.remainingBytes();
        }

        // first determine the buffer size
        int bufSize = -1;
        DynamicIntegerValue div = sde.getDynamicBufferSize();
        if (div != null) {
            long sizeInBits;
            try {
                sizeInBits = pcontext.resolveDynamicIntegerValue(div);
            } catch (XtceProcessingException e) {
                throw new CommandEncodingException(pcontext, e.getMessage());
            }
            if ((sizeInBits & 7) != 0) {
                throw new CommandEncodingException(pcontext,
                        "Size of string buffer (computed from " + div.getDynamicInstanceRef().getName() + ")"
                                + " is not a multiple of 8: " + sizeInBits);
            }
            bufSize = (int) (sizeInBits >>> 3);
            if (bufSize > bmr) {
                throw new CommandEncodingException(pcontext,
                        "Size of string buffer (computed from " + div.getDynamicInstanceRef().getName() + ")"
                                + " exceeds the "
                                + ((bmr == sde.getMaxSizeInBytes()) ? "max" : "remaining") + " size: "
                                + sizeInBits + ">" + (8 * bmr));
            }
            bmr = bufSize;
        } else if (sde.getSizeInBits() > 0) {
            bufSize = sde.getSizeInBits() >>> 3;
            if (bufSize > bmr) {
                throw new CommandEncodingException(pcontext, "Fixed size of string buffer exceeds the "
                        + ((bmr == sde.getMaxSizeInBytes()) ? "max" : "remaining") + " size: "
                        + sde.getSizeInBits() + ">" + (8 * bmr));
            }
            bmr = bufSize;
        }

        // then write the string
        int sizeInBytes;
        switch (sde.getSizeType()) {
        case FIXED:
            assert (bufSize > 0);
            sizeInBytes = rawValueBytes.length;
            if (sizeInBytes > bmr) {
                throw new CommandEncodingException(pcontext, "String size is greater that "
                        + (bmr == bufSize ? "buffer" : bmr == sde.getMaxSizeInBytes() ? "max" : "remaining")
                        + " size: " + sizeInBytes + ">" + bmr);
            }
            bitbuf.put(rawValueBytes);
            break;
        case LEADING_SIZE:
            sizeInBytes = rawValueBytes.length + sde.getSizeInBytesOfSizeTag();
            if (sizeInBytes > bmr) {
                throw new CommandEncodingException(pcontext, "String size + tag is greater that "
                        + (bmr == bufSize ? "buffer" : bmr == sde.getMaxSizeInBytes() ? "max" : "remaining")
                        + " size: " + sizeInBytes + ">" + bmr);
            }
            bitbuf.setByteOrder(sde.getByteOrder());
            bitbuf.putBits(sizeInBytes - sde.getSizeInBytesOfSizeTag(), sde.getSizeInBitsOfSizeTag());
            bitbuf.put(rawValueBytes);
            break;
        case TERMINATION_CHAR:
            sizeInBytes = rawValueBytes.length;
            if (bufSize < 0) {
                sizeInBytes++;
            }
            if (sizeInBytes > bmr) {
                throw new CommandEncodingException(pcontext, "String size "
                        + (bufSize < 0 ? "with terminator " : "")
                        + " is greater than "
                        + (bmr == bufSize ? "buffer" : bmr == sde.getMaxSizeInBytes() ? "max" : "remaining")
                        + " size: " + sizeInBytes + ">" + bmr);
            }

            bitbuf.put(rawValueBytes);
            if (bufSize < 0) {
                bitbuf.putByte(sde.getTerminationChar());
            } else if (sizeInBytes < bufSize) {
                bitbuf.putByte(sde.getTerminationChar());
                sizeInBytes++;
            }
            break;
        default:
            throw new IllegalStateException("Unsupported size type " + sde.getSizeType());
        }

        if (bufSize > sizeInBytes) { // fill up with nulls to reach the required size
            byte[] nulls = new byte[bufSize - sizeInBytes];
            bitbuf.put(nulls);
        }
    }

    private void encodeRawFloat(FloatDataEncoding de, Value rawValue) {
        switch (de.getEncoding()) {
        case IEEE754_1985:
        case MILSTD_1750A:
            encodeRawFloat2(de, rawValue);
            break;
        case STRING:
            encodeRaw(de.getStringDataEncoding(), rawValue);
            break;
        default:
            throw new IllegalArgumentException("Float Encoding " + de.getEncoding() + " not implemented");
        }
    }

    private void encodeRawFloat2(FloatDataEncoding de, Value rawValue) {
        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");
        }

        BitBuffer bitbuf = pcontext.bitbuf;
        bitbuf.setByteOrder(de.getByteOrder());
        if (de.getEncoding() == org.yamcs.xtce.FloatDataEncoding.Encoding.IEEE754_1985) {
            if (de.getSizeInBits() == 32) {
                bitbuf.putBits(Float.floatToIntBits((float) v), 32);
            } else {
                bitbuf.putBits(Double.doubleToLongBits(v), 64);
            }
        } else if (de.getEncoding() == org.yamcs.xtce.FloatDataEncoding.Encoding.MILSTD_1750A) {
            if (de.getSizeInBits() == 32) {
                bitbuf.putBits(MilStd1750A.encode32(v), 32);
            } else {
                bitbuf.putBits(MilStd1750A.encode48(v), 48);
            }
        }
    }

    private void encodeRawBinary(BinaryDataEncoding bde, Value rawValue, TcProcessingContext pcontext) {
        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());
        }
        BitBuffer bitbuf = pcontext.bitbuf;
        switch (bde.getType()) {
        case FIXED_SIZE:
            int sizeInBytes, bdeSizeInBytes;
            if (bde.getSizeInBits() < 0) { // if the size is negative, we take all the data
                bdeSizeInBytes = sizeInBytes = v.length;
            } else {
                bdeSizeInBytes = bde.getSizeInBits() / 8;
                sizeInBytes = Math.min(bdeSizeInBytes, v.length);
            }
            bitbuf.put(v, 0, sizeInBytes);
            if (bdeSizeInBytes > v.length) { // fill up with nulls to reach the required size
                byte[] nulls = new byte[bdeSizeInBytes - sizeInBytes];
                bitbuf.put(nulls);
            }
            break;
        case LEADING_SIZE:
            bitbuf.setByteOrder(ByteOrder.BIG_ENDIAN); // TBD
            bitbuf.putBits(v.length, bde.getSizeInBitsOfSizeTag()); // bde.getSizeInBitsOfSizeTag() can be 0 but bitbuf
                                                                    // won't mind
            bitbuf.put(v);
            break;
        case DYNAMIC:
            DynamicIntegerValue div = bde.getDynamicSize();
            ParameterOrArgumentRef ref = div.getDynamicInstanceRef();
            String sizeName = ref.getName();
            ArgumentValue sizeArgValue = pcontext.getArgumentValue(sizeName);
            if (sizeArgValue == null) {
                throw new IllegalStateException(
                        "No argument supplied for binary variable size: " + sizeName);
            }
            Value sizeValue = ref.useCalibratedValue() ? sizeArgValue.getEngValue() : sizeArgValue.getRawValue();
            long sizeInBits;
            try {
                sizeInBits = div.transform(sizeValue.toLong());
            } catch (UnsupportedOperationException e) {
                throw new IllegalStateException(
                        "Cannot convert argument " + sizeName + " of type " + sizeValue.getClass() + " to integer");
            }
            if (sizeInBits % 8 != 0) {
                throw new CommandEncodingException(
                        "Binary variable size argument is not a multiple of 8: " + sizeInBits);
            }
            if (sizeInBits < 0) {
                throw new CommandEncodingException(
                        "Binary variable size argument is negative: " + sizeInBits);
            }
            int dynSizeInBytes = (int) (sizeInBits / 8);
            int dataSizeInBytes = Math.min(dynSizeInBytes, v.length);
            bitbuf.put(v, 0, dataSizeInBytes);
            if (dynSizeInBytes > v.length) {
                // Fill with nulls to reach the required size.
                byte[] nuls = new byte[dynSizeInBytes - dataSizeInBytes];
                bitbuf.put(nuls);
            }
            break;
        default:
            throw new IllegalStateException("Unsupported size type " + bde.getType());
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy