/******************************************************************************** | |
* 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(); | |
} | |
} | |
} |