blob: f3d58b4633c11f750c751d90cd689f22a4e4f8bc [file] [log] [blame]
/********************************************************************************
* Copyright (c) 2015-2019 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
********************************************************************************/
package org.eclipse.mdm.businessobjects.utils;
import java.io.ByteArrayOutputStream;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import org.eclipse.mdm.api.base.ServiceNotProvidedException;
import org.eclipse.mdm.api.base.massdata.ReadRequest;
import org.eclipse.mdm.api.base.massdata.ReadRequest.ValuesMode;
import org.eclipse.mdm.api.base.massdata.ReadRequestBuilder;
import org.eclipse.mdm.api.base.model.AxisType;
import org.eclipse.mdm.api.base.model.Channel;
import org.eclipse.mdm.api.base.model.ChannelGroup;
import org.eclipse.mdm.api.base.model.DoubleComplex;
import org.eclipse.mdm.api.base.model.FloatComplex;
import org.eclipse.mdm.api.base.model.MeasuredValues;
import org.eclipse.mdm.api.base.model.MeasuredValues.ValueIterator;
import org.eclipse.mdm.api.base.model.ScalarType;
import org.eclipse.mdm.api.base.model.Unit;
import org.eclipse.mdm.api.dflt.ApplicationContext;
import org.eclipse.mdm.api.dflt.EntityManager;
import org.eclipse.mdm.protobuf.Mdm;
import org.eclipse.mdm.protobuf.Mdm.BooleanArray;
import org.eclipse.mdm.protobuf.Mdm.ByteArray;
import org.eclipse.mdm.protobuf.Mdm.ByteStreamArray;
import org.eclipse.mdm.protobuf.Mdm.DateArray;
import org.eclipse.mdm.protobuf.Mdm.DoubleArray;
import org.eclipse.mdm.protobuf.Mdm.DoubleComplexArray;
import org.eclipse.mdm.protobuf.Mdm.FloatArray;
import org.eclipse.mdm.protobuf.Mdm.FloatComplexArray;
import org.eclipse.mdm.protobuf.Mdm.IntegerArray;
import org.eclipse.mdm.protobuf.Mdm.LongArray;
import org.eclipse.mdm.protobuf.Mdm.MeasuredValuesList;
import org.eclipse.mdm.protobuf.Mdm.ShortArray;
import org.eclipse.mdm.protobuf.Mdm.StringArray;
import com.google.common.base.Strings;
import com.google.common.primitives.Doubles;
import com.google.protobuf.ByteString;
import com.google.protobuf.Timestamp;
/**
* Helper class for converting between protobuf and mdm types.
*
*/
public class ProtobufConverter {
/**
* Converted a DateArray to an array of {@link LocalDateTime}
*
* @param dateArray
* @param zoneId
* @return array of {@link LocalDateTime}
*/
public static LocalDateTime[] convertDates(DateArray dateArray, ZoneId zoneId) {
LocalDateTime[] strings = new LocalDateTime[dateArray.getValuesCount()];
for (int i = 0; i < dateArray.getValuesCount(); i++) {
Timestamp ts = dateArray.getValues(i);
strings[i] = Instant.ofEpochSecond(ts.getSeconds(), ts.getNanos()).atZone(zoneId).toLocalDateTime();
}
return strings;
}
/**
* Converts a {@link StringArray} to an array of strings
*
* @param stringArray
* @return array of strings
*/
public static String[] convertStrings(StringArray stringArray) {
String[] strings = new String[stringArray.getValuesCount()];
for (int i = 0; i < stringArray.getValuesCount(); i++) {
strings[i] = stringArray.getValues(i);
}
return strings;
}
/**
* Converts a list of {@link org.eclipse.mdm.protobuf.Mdm.FloatComplex} to an
* array of {@link FloatComplex}
*
* @param valuesList
* @return array of {@link FloatComplex}
*/
public static FloatComplex[] convertFloatComplex(List<org.eclipse.mdm.protobuf.Mdm.FloatComplex> valuesList) {
FloatComplex[] floatComplexes = new FloatComplex[valuesList.size()];
for (int i = 0; i < valuesList.size(); i++) {
floatComplexes[i] = new FloatComplex(valuesList.get(i).getRe(), valuesList.get(i).getIm());
}
return floatComplexes;
}
/**
* Converts a list of {@link org.eclipse.mdm.protobuf.Mdm.DoubleComplex} to an
* array of {@link DoubleComplex}
*
* @param valuesList
* @return array of {@link DoubleComplex}
*/
public static DoubleComplex[] convertDoubleComplex(List<org.eclipse.mdm.protobuf.Mdm.DoubleComplex> valuesList) {
DoubleComplex[] doubleComplexes = new DoubleComplex[valuesList.size()];
for (int i = 0; i < valuesList.size(); i++) {
doubleComplexes[i] = new DoubleComplex(valuesList.get(i).getRe(), valuesList.get(i).getIm());
}
return doubleComplexes;
}
/**
* Converts a list of {@link ByteString} to an array of array of byte.
*
* @param valuesList
* @return array of array of byte.
*/
public static byte[][] convertByteStreams(List<ByteString> valuesList) {
byte[][] byteStreams = new byte[valuesList.size()][];
for (int i = 0; i < valuesList.size(); i++) {
byteStreams[i] = valuesList.get(i).toByteArray();
}
return byteStreams;
}
/**
* Converts an {@link org.eclipse.mdm.protobuf.Mdm.AxisType} to {@link AxisType}
*
* @param axisType
* @return converted {@link AxisType}
*/
public static AxisType convert(Mdm.AxisType axisType) {
switch (axisType) {
case X_AXIS:
return AxisType.X_AXIS;
case Y_AXIS:
return AxisType.Y_AXIS;
case XY_AXIS:
return AxisType.XY_AXIS;
default:
throw new RuntimeException("Invalid value for AxisType: " + axisType.name());
}
}
/**
* Converts an {@link org.eclipse.mdm.protobuf.Mdm.ScalarType} to a
* {@link ScalarType}.
*
* @param scalarType
* @return converted {@link ScalarType}
*/
public static ScalarType convert(Mdm.ScalarType scalarType) {
switch (scalarType) {
case STRING:
return ScalarType.STRING;
case DATE:
return ScalarType.DATE;
case BOOLEAN:
return ScalarType.BOOLEAN;
case BYTE:
return ScalarType.BYTE;
case SHORT:
return ScalarType.SHORT;
case INTEGER:
return ScalarType.INTEGER;
case LONG:
return ScalarType.LONG;
case FLOAT:
return ScalarType.FLOAT;
case DOUBLE:
return ScalarType.DOUBLE;
case BYTE_STREAM:
return ScalarType.BYTE_STREAM;
case FLOAT_COMPLEX:
return ScalarType.FLOAT_COMPLEX;
case DOUBLE_COMPLEX:
return ScalarType.DOUBLE_COMPLEX;
case ENUMERATION:
return ScalarType.ENUMERATION;
case FILE_LINK:
return ScalarType.FILE_LINK;
case BLOB:
return ScalarType.BLOB;
case UNKNOWN:
return ScalarType.UNKNOWN;
default:
throw new RuntimeException("Invalid value for ScalarType: " + scalarType.name());
}
}
/**
* Converts a list of {@link MeasuredValues} to a {@link MeasuredValuesList}
*
* @param measuredValues
* @return converted {@link MeasuredValuesList}
*/
public static MeasuredValuesList convert(List<MeasuredValues> measuredValues) {
MeasuredValuesList.Builder builder = MeasuredValuesList.newBuilder();
for (MeasuredValues m : measuredValues) {
builder.addValues(convert(m));
}
return builder.build();
}
/**
* Converts {@link MeasuredValues} to
* {@link org.eclipse.mdm.protobuf.Mdm.MeasuredValues}
*
* @param m
* @return converted {@link org.eclipse.mdm.protobuf.Mdm.MeasuredValues}
*/
public static Mdm.MeasuredValues convert(MeasuredValues m) {
Mdm.MeasuredValues.Builder builder = Mdm.MeasuredValues.newBuilder().setName(m.getName()).setUnit(m.getUnit())
.setLength(m.getLength()).setAxisType(convert(m.getAxisType())).setIndependent(m.isIndependent())
.setScalarType(Mdm.ScalarType.valueOf(m.getScalarType().name()))
.addAllGenerationParameters(Doubles.asList(m.getGenerationParameters()));
BooleanArray.Builder flags = BooleanArray.newBuilder();
ValueIterator<Object> it = m.iterator();
if (m.getScalarType().isString()) {
StringArray.Builder strings = StringArray.newBuilder();
while (it.hasNext()) {
flags.addValues(it.isValid());
strings.addValues((String) it.next());
}
builder.setStringArray(strings);
} else if (m.getScalarType().isDate()) {
DateArray.Builder dates = DateArray.newBuilder();
while (it.hasNext()) {
flags.addValues(it.isValid());
LocalDateTime t = (LocalDateTime) it.next();
Instant time = t.toInstant(ZoneOffset.UTC);
dates.addValues(
Timestamp.newBuilder().setSeconds(time.getEpochSecond()).setNanos(time.getNano()).build());
}
builder.setDateArray(dates);
} else if (m.getScalarType().isBoolean()) {
BooleanArray.Builder booleans = BooleanArray.newBuilder();
while (it.hasNext()) {
flags.addValues(it.isValid());
booleans.addValues((boolean) it.next());
}
builder.setBooleanArray(booleans);
} else if (m.getScalarType().isByte()) {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
while (it.hasNext()) {
flags.addValues(it.isValid());
bytes.write((byte) it.next());
}
builder.setByteArray(ByteArray.newBuilder().setValues(ByteString.copyFrom(bytes.toByteArray())));
} else if (m.getScalarType().isShort()) {
ShortArray.Builder shorts = ShortArray.newBuilder();
while (it.hasNext()) {
flags.addValues(it.isValid());
shorts.addValues((short) it.next());
}
builder.setShortArray(shorts);
} else if (m.getScalarType().isInteger()) {
IntegerArray.Builder ints = IntegerArray.newBuilder();
while (it.hasNext()) {
flags.addValues(it.isValid());
int i = (int) it.next();
ints.addValues(i);
}
builder.setIntegerArray(ints);
} else if (m.getScalarType().isLong()) {
LongArray.Builder ints = LongArray.newBuilder();
while (it.hasNext()) {
flags.addValues(it.isValid());
ints.addValues((long) it.next());
}
builder.setLongArray(ints);
} else if (m.getScalarType().isFloat()) {
FloatArray.Builder floats = FloatArray.newBuilder();
while (it.hasNext()) {
flags.addValues(it.isValid());
floats.addValues((float) it.next());
}
builder.setFloatArray(floats);
} else if (m.getScalarType().isDouble()) {
DoubleArray.Builder doubles = DoubleArray.newBuilder();
while (it.hasNext()) {
flags.addValues(it.isValid());
doubles.addValues((double) it.next());
}
builder.setDoubleArray(doubles);
} else if (m.getScalarType().isByteStream()) {
ByteStreamArray.Builder bytestrs = ByteStreamArray.newBuilder();
while (it.hasNext()) {
flags.addValues(it.isValid());
bytestrs.addValues(ByteString.copyFrom((byte[]) it.next()));
}
builder.setByteStreamArray(bytestrs);
} else if (m.getScalarType().isFloatComplex()) {
FloatComplexArray.Builder floats = FloatComplexArray.newBuilder();
while (it.hasNext()) {
flags.addValues(it.isValid());
floats.addValues(convert((FloatComplex) it.next()));
}
builder.setFloatComplexArray(floats);
} else if (m.getScalarType().isDoubleComplex()) {
DoubleComplexArray.Builder doubles = DoubleComplexArray.newBuilder();
while (it.hasNext()) {
flags.addValues(it.isValid());
doubles.addValues(convert((DoubleComplex) it.next()));
}
builder.setDoubleComplexArray(doubles);
} else {
throw new IllegalArgumentException(
"MeasuredValues with scalarType '" + m.getScalarType() + "' not supported!");
}
builder.addAllFlags(flags.getValuesList());
return builder.build();
}
/**
* Converts between FloatComplex.
*
* @param complex
* @return
*/
public static Mdm.FloatComplex convert(FloatComplex complex) {
return Mdm.FloatComplex.newBuilder().setRe(complex.real()).setIm(complex.imaginary()).build();
}
/**
* Converts between DoubleComplex.
*
* @param complex
* @return
*/
public static Mdm.DoubleComplex convert(DoubleComplex complex) {
return Mdm.DoubleComplex.newBuilder().setRe(complex.real()).setIm(complex.imaginary()).build();
}
/**
* Converts between AxisType.
*
* @param axisType
* @return
*/
public static Mdm.AxisType convert(AxisType axisType) {
return Mdm.AxisType.valueOf(axisType.name());
}
/**
* Converts between ValuesMode.
*
* @param valuesMode
* @return
*/
public static ValuesMode convert(Mdm.ValuesMode valuesMode) {
return ValuesMode.valueOf(valuesMode.name());
}
/**
* Converts between ScalarType.
*
* @param value
* @return
*/
public static Mdm.ScalarType convert(ScalarType value) {
return Mdm.ScalarType.valueOf(value.name());
}
/**
* Converts a {@link org.eclipse.mdm.protobuf.Mdm.ReadRequest} to a
* {@link ReadRequest}
*
* @param context
* @param protoReadRequest
* @return converted {@link ReadRequest}
*/
public static ReadRequest convert(ApplicationContext context, Mdm.ReadRequest protoReadRequest) {
EntityManager em = context.getEntityManager()
.orElseThrow(() -> new ServiceNotProvidedException(EntityManager.class));
ChannelGroup channelGroup = em.load(ChannelGroup.class, protoReadRequest.getChannelGroupId());
ReadRequestBuilder rb = ReadRequest.create(channelGroup);
if (protoReadRequest.getChannelIdsCount() == 0) {
rb = rb.allChannels();
} else {
// Load Channels and group by ID. If multiple Channels with the same ID are
// loaded (which would be incorrect data), only the first one is used.
Map<String, Optional<Channel>> channels = em.load(Channel.class, protoReadRequest.getChannelIdsList())
.stream().collect(Collectors.groupingBy(Channel::getID, Collectors.reducing((c1, c2) -> c1)));
// Load Units and group by ID. If multiple Units with the same ID are
// loaded (which would be incorrect data), only the first one is used.
Map<String, Optional<Unit>> units = em.load(Unit.class, protoReadRequest.getUnitIdsList()).stream()
.collect(Collectors.groupingBy(Unit::getID, Collectors.reducing((u1, u2) -> u1)));
for (int i = 0; i < protoReadRequest.getChannelIdsCount(); i++) {
String channelId = protoReadRequest.getChannelIds(i);
Channel channel = channels.get(channelId).orElseThrow(
() -> new IllegalArgumentException("Channel with ID '" + channelId + "' does not exist!"));
final String unitId;
if (i < protoReadRequest.getUnitIdsCount()) {
unitId = protoReadRequest.getUnitIds(i);
} else {
unitId = null;
}
Unit unit;
if (Strings.isNullOrEmpty(unitId)) {
// no unit provided -> use unit from channel
unit = channel.getUnit();
} else {
unit = units.get(unitId).orElseThrow(
() -> new IllegalArgumentException("Unit with ID '" + unitId + "' does not exist!"));
}
rb = rb.channel(channel, unit);
}
}
return rb.valuesMode(convert(protoReadRequest.getValuesMode())).values(protoReadRequest.getStartIndex(),
protoReadRequest.getRequestSize());
}
}