| /******************************************************************************** |
| * 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.api.odsadapter.transaction; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.asam.ods.AoException; |
| import org.asam.ods.ApplElemAccess; |
| import org.asam.ods.Blob; |
| import org.asam.ods.DataType; |
| import org.asam.ods.ElemId; |
| import org.asam.ods.NameValueSeqUnit; |
| import org.asam.ods.SetType; |
| import org.asam.ods.TS_UnionSeq; |
| import org.asam.ods.TS_ValueSeq; |
| import org.asam.ods.T_DCOMPLEX; |
| import org.asam.ods.T_ExternalReference; |
| import org.asam.ods.T_LONGLONG; |
| import org.asam.ods.ValueMatrix; |
| import org.eclipse.mdm.api.base.massdata.WriteRequest; |
| import org.eclipse.mdm.api.base.model.Channel; |
| import org.eclipse.mdm.api.base.model.ChannelGroup; |
| import org.eclipse.mdm.api.base.model.ScalarType; |
| import org.eclipse.mdm.api.base.query.DataAccessException; |
| import org.eclipse.mdm.api.dflt.EntityManager; |
| import org.eclipse.mdm.api.odsadapter.query.ODSEntityType; |
| import org.eclipse.mdm.api.odsadapter.utils.ODSConverter; |
| |
| /** |
| * Appends values to existing measured values. |
| * |
| * @author Alexander Knoblauch, Peak Solution GmbH |
| */ |
| public final class AppendMeasuredValuesHandler { |
| |
| // ====================================================================== |
| // Instance variables |
| // ====================================================================== |
| private final ODSEntityType submatrixEntityType; |
| private final ODSTransaction transaction; |
| |
| private List<WriteRequest> writeRequests = new ArrayList<>(); |
| |
| // ====================================================================== |
| // Constructors |
| // ====================================================================== |
| |
| /** |
| * Constructor. |
| * |
| * @param transaction The owning {@link ODSTransaction}. |
| */ |
| public AppendMeasuredValuesHandler(ODSTransaction transaction) { |
| this.transaction = transaction; |
| submatrixEntityType = (ODSEntityType) transaction.getModelManager().getEntityType("SubMatrix"); |
| } |
| |
| // ====================================================================== |
| // Public methods |
| // ====================================================================== |
| |
| /** |
| * Adds given {@link WriteRequest} to be processed. |
| * |
| * @param writeRequest The {@code WriteRequest}. |
| */ |
| public void addRequest(WriteRequest writeRequest) { |
| writeRequests.add(writeRequest); |
| } |
| |
| /** |
| * Appends measured values to existing columns from registered |
| * {@link WriteRequest}s |
| * |
| * @throws AoException Thrown if the execution fails. |
| * @throws DataAccessException Thrown if the execution fails. |
| * @throws IOException Thrown if a file transfer operation fails. |
| */ |
| public void execute() throws AoException, DataAccessException, IOException { |
| |
| Map<String, List<WriteRequest>> channelGroupToWriteRequests = getChannelGroupToWriteRequests(writeRequests); |
| |
| channelGroupToWriteRequests.forEach((channelGroupId, writeRequests) -> { |
| try { |
| List<NameValueSeqUnit> valuesToAppend = getValuesToAppend(channelGroupId, writeRequests); |
| appendToChannelGroup(channelGroupId, valuesToAppend); |
| } catch (AoException e) { |
| throw new DataAccessException(e.getLocalizedMessage()); |
| } |
| }); |
| |
| this.writeRequests.clear(); |
| } |
| |
| // ====================================================================== |
| // Private methods |
| // ====================================================================== |
| |
| /** |
| * Generate a map, where the registered {@link WriteRequest}s are mapped to the |
| * channel group id |
| * |
| * @param writeRequests |
| * @return |
| */ |
| private Map<String, List<WriteRequest>> getChannelGroupToWriteRequests(List<WriteRequest> writeRequests) { |
| Map<String, List<WriteRequest>> returnVal = new HashMap<>(); |
| |
| Map<String, List<Channel>> channelMap = new HashMap<>(); |
| |
| EntityManager em = transaction.getContext().getEntityManager().get(); |
| |
| writeRequests.forEach(wr -> { |
| List<WriteRequest> list = returnVal.computeIfAbsent(wr.getChannelGroup().getID(), v -> new ArrayList<>()); |
| list.add(wr); |
| |
| channelMap.computeIfAbsent(wr.getChannelGroup().getID(), |
| v -> em.loadChildren(wr.getChannelGroup(), Channel.class)); |
| }); |
| |
| return returnVal; |
| } |
| |
| /** |
| * Getting all values to append as {@link NameValueSeqUnit} for the specific |
| * channelgroup id |
| * |
| * @param channelGroupId unique id of a channelgroup |
| * @param writeRequests {@link WriteRequest}s |
| * @return |
| */ |
| private List<NameValueSeqUnit> getValuesToAppend(String channelGroupId, List<WriteRequest> writeRequests) { |
| List<NameValueSeqUnit> returnVal = new ArrayList<>(); |
| |
| writeRequests.forEach(wr -> returnVal.add(getChannelValuesToAppend(wr.getChannel(), wr.getValues(), 0))); |
| |
| int maxLength = getMaxLength(returnVal); |
| |
| returnVal.forEach(nvusu -> { |
| if (maxLength > nvusu.value.flag.length) { |
| fillNameValueSeqUnit(nvusu, maxLength); |
| } |
| }); |
| |
| EntityManager em = transaction.getContext().getEntityManager().get(); |
| ChannelGroup channelGroup = em.load(ChannelGroup.class, channelGroupId); |
| |
| List<Channel> channels = em.loadChildren(channelGroup, Channel.class); |
| |
| channels.forEach(c -> { |
| if (!isChannelinValueSeq(returnVal, c)) { |
| returnVal.add(getChannelValuesToAppend(c, null, maxLength)); |
| } |
| }); |
| |
| return returnVal; |
| } |
| |
| /** |
| * Check if a {@link NameValueSeqUnit} for the channel exists in the list |
| * |
| * @param valueSeq |
| * @param channel |
| * @return true if exists, otherwise false |
| */ |
| private boolean isChannelinValueSeq(List<NameValueSeqUnit> valueSeq, Channel channel) { |
| boolean isChannelinValueSeq = false; |
| |
| String channelName = channel.getName(); |
| |
| for (NameValueSeqUnit nvsu : valueSeq) { |
| if (nvsu.valName.equals(channelName)) { |
| isChannelinValueSeq = true; |
| break; |
| } |
| } |
| |
| return isChannelinValueSeq; |
| } |
| |
| /** |
| * Fills the {@link NameValueSeqUnit} with invalid values, if the given length |
| * is higher then the length of the {@link NameValueSeqUnit} |
| * |
| * @param nvsu |
| * @param length |
| */ |
| private void fillNameValueSeqUnit(NameValueSeqUnit nvsu, int length) { |
| nvsu.value.flag = Arrays.copyOf(nvsu.value.flag, length); |
| |
| DataType dataType = nvsu.value.u.discriminator(); |
| |
| if (DataType.DT_FLOAT.equals(dataType)) { |
| nvsu.value.u.floatVal(Arrays.copyOf(nvsu.value.u.floatVal(), length)); |
| } else if (DataType.DT_BLOB.equals(dataType)) { |
| nvsu.value.u.blobVal(Arrays.copyOf(nvsu.value.u.blobVal(), length)); |
| } else if (DataType.DT_BOOLEAN.equals(dataType)) { |
| nvsu.value.u.booleanVal(Arrays.copyOf(nvsu.value.u.booleanVal(), length)); |
| } else if (DataType.DT_BYTE.equals(dataType)) { |
| nvsu.value.u.byteVal(Arrays.copyOf(nvsu.value.u.byteVal(), length)); |
| } else if (DataType.DT_BYTESTR.equals(dataType)) { |
| nvsu.value.u.bytestrVal(Arrays.copyOf(nvsu.value.u.bytestrVal(), length)); |
| } else if (DataType.DT_COMPLEX.equals(dataType)) { |
| nvsu.value.u.complexVal(Arrays.copyOf(nvsu.value.u.complexVal(), length)); |
| } else if (DataType.DT_DATE.equals(dataType)) { |
| nvsu.value.u.dateVal(Arrays.copyOf(nvsu.value.u.dateVal(), length)); |
| } else if (DataType.DT_DCOMPLEX.equals(dataType)) { |
| nvsu.value.u.dcomplexVal(Arrays.copyOf(nvsu.value.u.dcomplexVal(), length)); |
| } else if (DataType.DT_DOUBLE.equals(dataType)) { |
| nvsu.value.u.doubleVal(Arrays.copyOf(nvsu.value.u.doubleVal(), length)); |
| } else if (DataType.DT_EXTERNALREFERENCE.equals(dataType)) { |
| nvsu.value.u.extRefVal(Arrays.copyOf(nvsu.value.u.extRefVal(), length)); |
| } else if (DataType.DT_LONG.equals(dataType)) { |
| nvsu.value.u.longVal(Arrays.copyOf(nvsu.value.u.longVal(), length)); |
| } else if (DataType.DT_LONGLONG.equals(dataType)) { |
| nvsu.value.u.longlongVal(Arrays.copyOf(nvsu.value.u.longlongVal(), length)); |
| } else if (DataType.DT_SHORT.equals(dataType)) { |
| nvsu.value.u.shortVal(Arrays.copyOf(nvsu.value.u.shortVal(), length)); |
| } else if (DataType.DT_STRING.equals(dataType)) { |
| nvsu.value.u.stringVal(Arrays.copyOf(nvsu.value.u.stringVal(), length)); |
| } else { |
| new DataAccessException("Not supported Datatype: " + dataType); |
| } |
| |
| } |
| |
| /** |
| * |
| * @param valSeq |
| * @return |
| */ |
| private int getMaxLength(List<NameValueSeqUnit> valSeq) { |
| int maxLength = 0; |
| |
| for (NameValueSeqUnit nvsu : valSeq) { |
| if (nvsu.value.flag.length > maxLength) { |
| maxLength = nvsu.value.flag.length; |
| } |
| } |
| |
| return maxLength; |
| } |
| |
| /** |
| * Appending the ValuesToAppend to the value matrix |
| * |
| * @param channelGroupId |
| * @param valuesToAppend |
| * @throws AoException |
| */ |
| private void appendToChannelGroup(String channelGroupId, List<NameValueSeqUnit> valuesToAppend) throws AoException { |
| T_LONGLONG aIDSubMatrix = submatrixEntityType.getODSID(); |
| |
| ValueMatrix valueMatrix = getApplElemAccess() |
| .getValueMatrix(new ElemId(aIDSubMatrix, ODSConverter.toODSID(channelGroupId))); |
| |
| valueMatrix.setValue(SetType.APPEND, valueMatrix.getRowCount(), |
| valuesToAppend.toArray(new NameValueSeqUnit[valuesToAppend.size()])); |
| } |
| |
| /** |
| * Converting the values to a {@link NameValueSeqUnit}, if values are null the |
| * {@link NameValueSeqUnit} will contains only invalid values |
| * |
| * @param channel {@link Channel} |
| * @param values to convert |
| * @param length of the {@link NameValueSeqUnit} |
| * @return |
| */ |
| private NameValueSeqUnit getChannelValuesToAppend(Channel channel, Object values, int length) { |
| NameValueSeqUnit nvsu = new NameValueSeqUnit(); |
| String unitName = channel.getUnit().getName(); |
| nvsu.valName = channel.getName(); |
| nvsu.unit = unitName; |
| nvsu.value = new TS_ValueSeq(); |
| |
| if (ScalarType.FLOAT.equals(channel.getScalarType())) { |
| float[] vals = new float[length]; |
| |
| if (values != null) { |
| vals = (float[]) values; |
| } |
| nvsu.value.flag = new short[vals.length]; |
| nvsu.value.u = new TS_UnionSeq(); |
| nvsu.value.u.floatVal(vals); |
| |
| } else if (ScalarType.INTEGER.equals(channel.getScalarType())) { |
| int[] vals = new int[length]; |
| |
| if (values != null) { |
| vals = (int[]) values; |
| } |
| |
| nvsu.value.flag = new short[vals.length]; |
| nvsu.value.u = new TS_UnionSeq(); |
| nvsu.value.u.longVal(vals); |
| } else if (ScalarType.BLOB.equals(channel.getScalarType())) { |
| Blob[] vals = new Blob[length]; |
| |
| if (values != null) { |
| vals = (Blob[]) values; |
| } |
| |
| nvsu.value.flag = new short[vals.length]; |
| nvsu.value.u = new TS_UnionSeq(); |
| nvsu.value.u.blobVal(vals); |
| } else if (ScalarType.BOOLEAN.equals(channel.getScalarType())) { |
| boolean[] vals = new boolean[length]; |
| |
| if (values != null) { |
| vals = (boolean[]) values; |
| } |
| |
| nvsu.value.flag = new short[vals.length]; |
| nvsu.value.u = new TS_UnionSeq(); |
| nvsu.value.u.booleanVal(vals); |
| } else if (ScalarType.BYTE.equals(channel.getScalarType())) { |
| byte[] vals = new byte[length]; |
| |
| if (values != null) { |
| vals = (byte[]) values; |
| } |
| |
| nvsu.value.flag = new short[vals.length]; |
| nvsu.value.u = new TS_UnionSeq(); |
| nvsu.value.u.byteVal(vals); |
| } else if (ScalarType.DATE.equals(channel.getScalarType())) { |
| String[] vals = new String[length]; |
| |
| if (values != null) { |
| vals = (String[]) values; |
| } |
| |
| nvsu.value.flag = new short[vals.length]; |
| nvsu.value.u = new TS_UnionSeq(); |
| nvsu.value.u.dateVal(vals); |
| } else if (ScalarType.DOUBLE.equals(channel.getScalarType())) { |
| double[] vals = new double[length]; |
| |
| if (values != null) { |
| vals = (double[]) values; |
| } |
| |
| nvsu.value.flag = new short[vals.length]; |
| nvsu.value.u = new TS_UnionSeq(); |
| nvsu.value.u.doubleVal(vals); |
| } else if (ScalarType.DOUBLE_COMPLEX.equals(channel.getScalarType())) { |
| T_DCOMPLEX[] vals = new T_DCOMPLEX[length]; |
| |
| if (values != null) { |
| vals = (T_DCOMPLEX[]) values; |
| } |
| |
| nvsu.value.flag = new short[vals.length]; |
| nvsu.value.u = new TS_UnionSeq(); |
| nvsu.value.u.dcomplexVal(vals); |
| } else if (ScalarType.FILE_LINK.equals(channel.getScalarType())) { |
| T_ExternalReference[] vals = new T_ExternalReference[length]; |
| |
| if (values != null) { |
| vals = (T_ExternalReference[]) values; |
| } |
| |
| nvsu.value.flag = new short[vals.length]; |
| nvsu.value.u = new TS_UnionSeq(); |
| nvsu.value.u.extRefVal(vals); |
| } else if (ScalarType.LONG.equals(channel.getScalarType())) { |
| T_LONGLONG[] vals = new T_LONGLONG[length]; |
| |
| if (values != null) { |
| vals = (T_LONGLONG[]) values; |
| } |
| |
| nvsu.value.flag = new short[vals.length]; |
| nvsu.value.u = new TS_UnionSeq(); |
| nvsu.value.u.longlongVal(vals); |
| } else if (ScalarType.SHORT.equals(channel.getScalarType())) { |
| short[] vals = new short[length]; |
| |
| if (values != null) { |
| vals = (short[]) values; |
| } |
| |
| nvsu.value.flag = new short[vals.length]; |
| nvsu.value.u = new TS_UnionSeq(); |
| nvsu.value.u.shortVal(vals); |
| } else if (ScalarType.STRING.equals(channel.getScalarType())) { |
| String[] vals = new String[length]; |
| |
| if (values != null) { |
| vals = (String[]) values; |
| } |
| |
| nvsu.value.flag = new short[vals.length]; |
| nvsu.value.u = new TS_UnionSeq(); |
| nvsu.value.u.stringVal(vals); |
| } else { |
| throw new DataAccessException("Not supported DataType " + values.getClass()); |
| } |
| |
| if (values != null) { |
| Arrays.fill(nvsu.value.flag, (short) 15); |
| } |
| |
| return nvsu; |
| } |
| |
| /** |
| * Returns the {@link ApplElemAccess}. |
| * |
| * @return The {@code ApplElemAccess} is returned. |
| * @throws AoException Thrown in case of errors. |
| */ |
| private ApplElemAccess getApplElemAccess() throws AoException { |
| return transaction.getContext().getODSModelManager().getApplElemAccess(); |
| } |
| |
| } |