blob: 5f96b50791dc77ebe9ca984425b264f540c1e4da [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.boundary;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import javax.ejb.Stateless;
import javax.inject.Inject;
import org.eclipse.mdm.api.base.Transaction;
import org.eclipse.mdm.api.base.massdata.AnyTypeValuesBuilder;
import org.eclipse.mdm.api.base.massdata.ComplexNumericalValuesBuilder;
import org.eclipse.mdm.api.base.massdata.IndependentBuilder;
import org.eclipse.mdm.api.base.massdata.ReadRequest;
import org.eclipse.mdm.api.base.massdata.WriteRequest;
import org.eclipse.mdm.api.base.massdata.WriteRequestBuilder;
import org.eclipse.mdm.api.base.massdata.WriteRequestFinalizer;
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.Environment;
import org.eclipse.mdm.api.base.model.FloatComplex;
import org.eclipse.mdm.api.base.model.MeasuredValues;
import org.eclipse.mdm.api.dflt.ApplicationContext;
import org.eclipse.mdm.api.dflt.EntityManager;
import org.eclipse.mdm.businessobjects.utils.PreviewHelper;
import org.eclipse.mdm.businessobjects.utils.ProtobufConverter;
import org.eclipse.mdm.connector.boundary.ConnectorService;
import org.eclipse.mdm.protobuf.Mdm;
import org.eclipse.mdm.protobuf.Mdm.MeasuredValuesList;
import org.eclipse.mdm.protobuf.Mdm.PreviewRequest;
import org.eclipse.mdm.protobuf.Mdm.PreviewValuesList;
import org.eclipse.mdm.protobuf.Mdm.WriteRequestList.WriteRequest.ExplicitData;
import org.eclipse.mdm.protobuf.Mdm.WriteRequestList.WriteRequest.ImplicitConstant;
import org.eclipse.mdm.protobuf.Mdm.WriteRequestList.WriteRequest.ImplicitLinear;
import org.eclipse.mdm.protobuf.Mdm.WriteRequestList.WriteRequest.ImplicitSaw;
import org.eclipse.mdm.protobuf.Mdm.WriteRequestList.WriteRequest.RawLinear;
import org.eclipse.mdm.protobuf.Mdm.WriteRequestList.WriteRequest.RawLinearCalibrated;
import org.eclipse.mdm.protobuf.Mdm.WriteRequestList.WriteRequest.RawPolynomial;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.primitives.Booleans;
import com.google.common.primitives.Doubles;
import com.google.common.primitives.Floats;
import com.google.common.primitives.Ints;
import com.google.common.primitives.Longs;
import com.google.common.primitives.Shorts;
@Stateless
public class ValuesService {
private static final Logger LOG = LoggerFactory.getLogger(ValuesService.class);
@Inject
private ConnectorService connectorService;
/**
* Loads multiple MeasuredValues as specified in ReadRequest.
*
* @param sourceName name of the source (MDM {@link Environment} name)
* @param protoReadRequest Protobuf ReadRequest
* @return the loaded MeasuredValuesList
*/
public MeasuredValuesList load(String sourceName, Mdm.ReadRequest protoReadRequest) {
ApplicationContext context = connectorService.getContextByName(sourceName);
ReadRequest readRequest = ProtobufConverter.convert(context, protoReadRequest);
List<MeasuredValues> measuredValues = context.getEntityManager().get().readMeasuredValues(readRequest);
return ProtobufConverter.convert(measuredValues);
}
/**
* Loads preview values for multiple channels as specified in
* {@link PreviewRequest}. The preview consists of minimum (min), maximum (max)
* and average (avg) values for each chunk of values. The number of chunks must
* be given in {@link PreviewRequest}.
*
* @param sourceName name of the source (MDM {@link Environment} name)
* @param protoReadRequest Protobuf {@link PreviewRequest}
* @return the loaded PreviewValuesList
*/
public PreviewValuesList preview(String sourceName, Mdm.PreviewRequest previewRequest) {
ApplicationContext context = connectorService.getContextByName(sourceName);
ReadRequest readRequest = ProtobufConverter.convert(context, previewRequest.getReadRequest());
List<Mdm.MeasuredValues> measuredValues = ProtobufConverter
.convert(context.getEntityManager().get().readMeasuredValues(readRequest)).getValuesList();
// Calculate Preview
return new PreviewHelper().calculatePreview(measuredValues, previewRequest.getNumberOfChunks());
}
/**
* Writes values for multiple Channels.
*
* @param sourceName name of the source (MDM {@link Environment}
* name)
* @param protoWriteRequestList Protobuf {@link WriteRequest}
*/
public void write(String sourceName, Mdm.WriteRequestList protoWriteRequestList) {
ApplicationContext context = connectorService.getContextByName(sourceName);
Supplier<ZoneId> zoneId = Suppliers
.memoize(() -> getServerZoneId(context.getEntityManager().get().loadEnvironment()));
List<WriteRequest> writeRequests = new ArrayList<>();
for (Mdm.WriteRequestList.WriteRequest r : protoWriteRequestList.getValuesList()) {
writeRequests.add(convert(context, r, zoneId));
}
Transaction t = context.getEntityManager().get().startTransaction();
t.writeMeasuredValues(writeRequests);
t.commit();
}
/**
* Converts a Protobuf to a api.base WritRequest.
*
* @param context {@link ApplicationContext} to write to.
* @param protoWriteRequest Data to write.
* @param zoneId A supplier for a {@link ZoneId} which will be used,
* if Data with DT_DATE is written.
* @return converted {@link WriteRequest}
*/
private WriteRequest convert(ApplicationContext context, Mdm.WriteRequestList.WriteRequest protoWriteRequest,
Supplier<ZoneId> zoneId) {
EntityManager em = context.getEntityManager().get();
ChannelGroup channelGroup = em.load(ChannelGroup.class, protoWriteRequest.getChannelGroupId());
Channel channel = em.load(Channel.class, protoWriteRequest.getChannelId());
AxisType axisType = ProtobufConverter.convert(protoWriteRequest.getAxisType());
WriteRequestBuilder rb = WriteRequest.create(channelGroup, channel, axisType);
WriteRequestFinalizer wrf;
switch (protoWriteRequest.getDataCase()) {
case EXPLICIT:
wrf = setValues(rb.explicit(), protoWriteRequest.getExplicit(), zoneId);
break;
case IMPLICIT_CONSTANT:
ImplicitConstant ic = protoWriteRequest.getImplicitConstant();
wrf = rb.implicitConstant(ProtobufConverter.convert(ic.getScalarType()), ic.getOffset());
break;
case IMPLICIT_LINEAR:
ImplicitLinear il = protoWriteRequest.getImplicitLinear();
wrf = rb.implicitLinear(ProtobufConverter.convert(il.getScalarType()), il.getStart(), il.getIncrement());
break;
case IMPLICIT_SAW:
ImplicitSaw is = protoWriteRequest.getImplicitSaw();
wrf = rb.implicitSaw(ProtobufConverter.convert(is.getScalarType()), is.getStart(), is.getIncrement(),
is.getStop());
break;
case RAW_LINEAR:
RawLinear rl = protoWriteRequest.getRawLinear();
wrf = setValues(rb.rawLinear(rl.getOffset(), rl.getFactor()), rl.getValues(), zoneId);
break;
case RAW_LINEAR_CALIBRATED:
RawLinearCalibrated rlc = protoWriteRequest.getRawLinearCalibrated();
wrf = setValues(rb.rawLinearCalibrated(rlc.getOffset(), rlc.getFactor(), rlc.getCalibration()),
rlc.getValues(), zoneId);
break;
case RAW_POLYNOMIAL:
RawPolynomial rp = protoWriteRequest.getRawPolynomial();
wrf = setValues(rb.rawPolynomial(Doubles.toArray(rp.getCoefficientsList())), rp.getValues(), zoneId);
break;
case DATA_NOT_SET:
default:
throw new RuntimeException("Not supported yet: " + protoWriteRequest.getDataCase());
}
// TODO mkoller 04.11.2019, Unit conversion is not yet supported.
// if (wrf instanceof UnitBuilder) {
// // Unit convertion not supported yet!
// Unit unit = context.getEntityManager().get().load(Unit.class, "" + protoWriteRequest.getUnitId());
// wrf = ((UnitBuilder) wrf).sourceUnit(unit);
// }
// if (wrf instanceof UnitIndependentBuilder) {
// // Unit convertion not supported yet!
// Unit unit = context.getEntityManager().get().load(Unit.class, "" + protoWriteRequest.getUnitId());
// wrf = ((UnitBuilder) wrf).sourceUnit(unit);
// }
if (wrf instanceof IndependentBuilder) {
wrf = ((IndependentBuilder) wrf).independent(protoWriteRequest.getIndependent());
}
return wrf.build();
}
/**
* Helper function to set values for the appropriate datatype in an
* {@link AnyTypeValuesBuilder}
*
* @param builder
* @param explicitData
* @param zoneId
* @return {@link WriteRequestFinalizer}
*/
private WriteRequestFinalizer setValues(AnyTypeValuesBuilder builder, ExplicitData explicitData,
Supplier<ZoneId> zoneId) {
boolean[] flags = Booleans.toArray(explicitData.getFlagsList());
switch (explicitData.getValuesCase()) {
case STRING_ARRAY:
String[] strings = ProtobufConverter.convertStrings(explicitData.getStringArray());
return (flags.length > 0) ? builder.stringValues(strings, flags) : builder.stringValues(strings);
case DATE_ARRAY:
LocalDateTime[] dates = ProtobufConverter.convertDates(explicitData.getDateArray(), zoneId.get());
return (flags.length > 0) ? builder.dateValues(dates, flags) : builder.dateValues(dates);
case BOOLEAN_ARRAY:
boolean[] booleans = Booleans.toArray(explicitData.getBooleanArray().getValuesList());
return (flags.length > 0) ? builder.booleanValues(booleans, flags) : builder.booleanValues(booleans);
case BYTE_STREAM_ARRAY:
byte[][] byteStreams = ProtobufConverter
.convertByteStreams(explicitData.getByteStreamArray().getValuesList());
return (flags.length > 0) ? builder.byteStreamValues(byteStreams, flags)
: builder.byteStreamValues(byteStreams);
case BYTE_ARRAY:
case SHORT_ARRAY:
case INTEGER_ARRAY:
case LONG_ARRAY:
case FLOAT_ARRAY:
case DOUBLE_ARRAY:
case FLOAT_COMPLEX_ARRAY:
case DOUBLE_COMPLEX_ARRAY:
return setValues((ComplexNumericalValuesBuilder) builder, explicitData, zoneId);
case VALUES_NOT_SET:
default:
throw new RuntimeException("No explicit data set!");
}
}
/**
* Helper function to set values for the appropriate datatype in a
* {@link ComplexNumericalValuesBuilder}.
*
* @param builder
* @param explicitData
* @param zoneId
* @return {@link WriteRequestFinalizer}
*/
private WriteRequestFinalizer setValues(ComplexNumericalValuesBuilder builder, ExplicitData explicitData,
Supplier<ZoneId> zoneId) {
boolean[] flags = Booleans.toArray(explicitData.getFlagsList());
switch (explicitData.getValuesCase()) {
case STRING_ARRAY:
case DATE_ARRAY:
case BOOLEAN_ARRAY:
case BYTE_STREAM_ARRAY:
throw new RuntimeException(
explicitData.getValuesCase() + " not supported by ComplexNumericalValuesBuilder!");
case BYTE_ARRAY:
byte[] bytes = explicitData.getByteArray().getValues().toByteArray();
return (flags.length > 0) ? builder.byteValues(bytes, flags) : builder.byteValues(bytes);
case SHORT_ARRAY:
short[] shorts = Shorts.toArray(explicitData.getShortArray().getValuesList());
return (flags.length > 0) ? builder.shortValues(shorts, flags) : builder.shortValues(shorts);
case INTEGER_ARRAY:
int[] ints = Ints.toArray(explicitData.getIntegerArray().getValuesList());
return (flags.length > 0) ? builder.integerValues(ints, flags) : builder.integerValues(ints);
case LONG_ARRAY:
long[] longs = Longs.toArray(explicitData.getLongArray().getValuesList());
return (flags.length > 0) ? builder.longValues(longs, flags) : builder.longValues(longs);
case FLOAT_ARRAY:
float[] floats = Floats.toArray(explicitData.getFloatArray().getValuesList());
return (flags.length > 0) ? builder.floatValues(floats, flags) : builder.floatValues(floats);
case DOUBLE_ARRAY:
double[] doubles = Doubles.toArray(explicitData.getDoubleArray().getValuesList());
return (flags.length > 0) ? builder.doubleValues(doubles, flags) : builder.doubleValues(doubles);
case FLOAT_COMPLEX_ARRAY:
FloatComplex[] floatComplexes = ProtobufConverter
.convertFloatComplex(explicitData.getFloatComplexArray().getValuesList());
return (flags.length > 0) ? builder.floatComplexValues(floatComplexes, flags)
: builder.floatComplexValues(floatComplexes);
case DOUBLE_COMPLEX_ARRAY:
DoubleComplex[] doubleComplexes = ProtobufConverter
.convertDoubleComplex(explicitData.getDoubleComplexArray().getValuesList());
return (flags.length > 0) ? builder.doubleComplexValues(doubleComplexes, flags)
: builder.doubleComplexValues(doubleComplexes);
case VALUES_NOT_SET:
default:
throw new RuntimeException("No explicit data set!");
}
}
/**
* Returns the ZonId configured in the {@link Environment}
*
* @param env {@link Environment} for extracting the ZoneId
* @return ZonId configured in the {@link Environment}
*/
private ZoneId getServerZoneId(Environment env) {
String timezone = env.getTimezone();
try {
return ZoneId.of(timezone);
} catch (Exception e) {
if (LOG.isDebugEnabled()) {
LOG.debug("Timezone '" + timezone + "' of Environment '" + env.getName() + "' is invalid!", e);
} else {
LOG.warn("Timezone '" + timezone + "' of Environment '" + env.getName() + "' is invalid!");
}
return ZoneId.systemDefault();
}
}
}