| /******************************************************************************** |
| * 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; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Set; |
| |
| import org.apache.commons.lang3.tuple.ImmutablePair; |
| import org.apache.commons.lang3.tuple.Pair; |
| import org.asam.ods.AoException; |
| import org.asam.ods.Column; |
| import org.asam.ods.ElemId; |
| import org.asam.ods.NameValueSeqUnit; |
| import org.asam.ods.T_LONGLONG; |
| import org.asam.ods.ValueMatrix; |
| import org.asam.ods.ValueMatrixMode; |
| import org.eclipse.mdm.api.base.adapter.Attribute; |
| import org.eclipse.mdm.api.base.adapter.EntityType; |
| import org.eclipse.mdm.api.base.massdata.ReadRequest; |
| import org.eclipse.mdm.api.base.massdata.ReadRequest.ValuesMode; |
| import org.eclipse.mdm.api.base.model.AxisType; |
| import org.eclipse.mdm.api.base.model.Channel; |
| import org.eclipse.mdm.api.base.model.Entity; |
| import org.eclipse.mdm.api.base.model.MeasuredValues; |
| import org.eclipse.mdm.api.base.model.SequenceRepresentation; |
| import org.eclipse.mdm.api.base.model.Unit; |
| import org.eclipse.mdm.api.base.model.Value; |
| import org.eclipse.mdm.api.base.query.ComparisonOperator; |
| import org.eclipse.mdm.api.base.query.DataAccessException; |
| import org.eclipse.mdm.api.base.query.Filter; |
| import org.eclipse.mdm.api.base.query.Query; |
| import org.eclipse.mdm.api.base.query.QueryService; |
| import org.eclipse.mdm.api.base.query.Result; |
| import org.eclipse.mdm.api.odsadapter.query.ODSEntityType; |
| import org.eclipse.mdm.api.odsadapter.query.ODSModelManager; |
| import org.eclipse.mdm.api.odsadapter.utils.ODSConverter; |
| |
| import com.google.common.collect.Lists; |
| |
| /** |
| * Reads mass data specified in {@link ReadRequest}s. |
| * |
| * @since 1.0.0 |
| * @author Viktor Stoehr, Gigatronik Ingolstadt GmbH |
| */ |
| public final class ReadRequestHandler { |
| public static final class ColumnAttributes { |
| private String name; |
| private SequenceRepresentation sequenceRepresentation; |
| private double[] generationParameters; |
| private boolean independent; |
| private AxisType axisType; |
| |
| public ColumnAttributes(String name, SequenceRepresentation sequenceRepresentation, |
| double[] generationParameters, boolean independent, AxisType axisType) { |
| this.name = name; |
| this.sequenceRepresentation = sequenceRepresentation; |
| this.generationParameters = generationParameters; |
| this.independent = independent; |
| this.axisType = axisType; |
| } |
| |
| public String getName() { |
| return name; |
| } |
| |
| public void setName(String name) { |
| this.name = name; |
| } |
| |
| public SequenceRepresentation getSequenceRepresentation() { |
| return sequenceRepresentation; |
| } |
| |
| public void setSequenceRepresentation(SequenceRepresentation sequenceRepresentation) { |
| this.sequenceRepresentation = sequenceRepresentation; |
| } |
| |
| public double[] getGenerationParameters() { |
| return generationParameters; |
| } |
| |
| public void setGenerationParameters(double[] generationParameters) { |
| this.generationParameters = generationParameters; |
| } |
| |
| public boolean isIndependentColumn() { |
| return independent; |
| } |
| |
| public void setIndependentColumn(boolean independent) { |
| this.independent = independent; |
| } |
| |
| public AxisType getAxisType() { |
| return axisType; |
| } |
| |
| public void setAxisType(AxisType axisType) { |
| this.axisType = axisType; |
| } |
| |
| } |
| |
| // ====================================================================== |
| // Instance variables |
| // ====================================================================== |
| |
| private final ODSModelManager modelManager; |
| private final QueryService queryService; |
| |
| // ====================================================================== |
| // Constructors |
| // ====================================================================== |
| |
| /** |
| * Constructor. |
| * |
| * @param modelManager Used to gain access to value matrices. |
| */ |
| public ReadRequestHandler(ODSModelManager modelManager, QueryService queryService) { |
| this.modelManager = modelManager; |
| this.queryService = queryService; |
| } |
| |
| // ====================================================================== |
| // Public methods |
| // ====================================================================== |
| |
| /** |
| * Loads {@link MeasuredValues} as defined in given {@link ReadRequest}. |
| * |
| * @param readRequest The {@code MeasuredValues} request configuration. |
| * @return The loaded {@code MeasuredValues} are returned. |
| * @throws DataAccessException Thrown if unable to load {@code |
| * MeasuredValues} . |
| */ |
| public List<MeasuredValues> execute(ReadRequest readRequest) throws DataAccessException { |
| ValueMatrix valueMatrix = null; |
| Column[] arrColumns = null; |
| |
| try { |
| valueMatrix = getValueMatrix(readRequest); |
| List<Pair<Column, ColumnAttributes>> listColumnPairs = getODSColumns(readRequest, valueMatrix); |
| |
| arrColumns = listColumnPairs.stream().map(Pair::getLeft).toArray(Column[]::new); |
| ColumnAttributes[] arrColumnAttributes = listColumnPairs.stream().map(Pair::getRight) |
| .toArray(ColumnAttributes[]::new); |
| |
| NameValueSeqUnit[] nvsus = valueMatrix.getValue(arrColumns, readRequest.getStartIndex(), |
| readRequest.getRequestSize()); |
| return ODSConverter.fromODSMeasuredValuesSeq(nvsus, arrColumnAttributes); |
| } catch (AoException aoe) { |
| throw new DataAccessException(aoe.reason, aoe); |
| } finally { |
| releaseColumns(arrColumns); |
| releaseValueMatrix(valueMatrix); |
| } |
| } |
| |
| // ====================================================================== |
| // Private methods |
| // ====================================================================== |
| |
| /** |
| * Loads all for each defined {@link Channel} in given {@link ReadRequest} and |
| * loads the corresponding {@link Column} using given {@link ValueMatrix}. |
| * |
| * @param readRequest Defines required {@code Column}s. |
| * @param valueMatrix Used to load required {@code Column}s. |
| * @return {@code Column} configured in given {@code ReadRequest} are returned |
| * with defined {@link Unit} setup. |
| * @throws AoException Throw if unable to load all available |
| * {@code Column}s. |
| * @throws DataAccessException Thrown on wrong {@code ReadRequest} setup. |
| */ |
| private List<Pair<Column, ColumnAttributes>> getODSColumns(ReadRequest readRequest, ValueMatrix valueMatrix) |
| throws AoException, DataAccessException { |
| List<Pair<Column, ColumnAttributes>> listColumnPairs = new ArrayList<>(); |
| Map<String, Column> mapColumns = new HashMap<>(); |
| |
| try { |
| if (readRequest.isLoadAllChannels()) { |
| // TODO should it be possible to overwrite the unit of some |
| // channels?! |
| // -> this results in a performance issue since we need to call |
| // getName() |
| // on each column for mapping! (no longer, see below!) |
| Column[] columns = valueMatrix.getColumns("*"); |
| |
| if (null != columns) { |
| for (Column column : columns) { |
| String columnName = column.getName(); |
| if (mapColumns.containsKey(columnName)) { |
| releaseColumns(columns); |
| throw new DataAccessException( |
| String.format("Duplicate column name '%s' found within submatrix ID %d!", |
| columnName, Long.valueOf(readRequest.getChannelGroup().getID()))); |
| } |
| |
| mapColumns.put(columnName, column); |
| } |
| } |
| } else { |
| for (Entry<Channel, Unit> entry : readRequest.getChannels().entrySet()) { |
| Channel channel = entry.getKey(); |
| Unit unit = entry.getValue(); |
| String channelName = channel.getName(); |
| Column[] columns = valueMatrix.getColumns(channelName); |
| if (columns == null || columns.length != 1) { |
| releaseColumns(columns); |
| throw new DataAccessException(String.format( |
| "Zero or more than one column with name '%s' found within submatrix ID %d!", |
| channelName, Long.valueOf(readRequest.getChannelGroup().getID()))); |
| } |
| |
| Column column = columns[0]; |
| if (!unit.nameEquals(channel.getUnit().getName())) { |
| column.setUnit(unit.getName()); |
| } |
| |
| mapColumns.put(channelName, column); |
| } |
| } |
| |
| if (mapColumns.size() > 0) { |
| EntityType localColumnEntityType = modelManager.getEntityType("LocalColumn"); |
| Attribute idAttr = localColumnEntityType.getAttribute("Id"); |
| Attribute nameAttr = localColumnEntityType.getAttribute("Name"); |
| Attribute submatrixAttr = localColumnEntityType.getAttribute("SubMatrix"); |
| |
| // Don't query GenerationParameters together with other non-ID attributes as |
| // Avalon dislikes this: |
| Query query1 = queryService.createQuery() |
| .select(Lists.newArrayList(idAttr, nameAttr, |
| localColumnEntityType.getAttribute("SequenceRepresentation"), |
| localColumnEntityType.getAttribute("IndependentFlag"), |
| localColumnEntityType.getAttribute("axistype"))); |
| |
| Filter filter = Filter.and().add(ComparisonOperator.EQUAL.create(submatrixAttr, |
| Long.valueOf(readRequest.getChannelGroup().getID()))); |
| |
| Set<String> setColumnNames = mapColumns.keySet(); |
| |
| Map<String, ColumnAttributes> mapColumnAttributes = new HashMap<>(); |
| |
| for (Result result : query1.fetch(filter)) { |
| Map<String, Value> mapValues = result.getRecord(localColumnEntityType).getValues(); |
| |
| String columnName = mapValues.get("Name").extract(); |
| |
| if (setColumnNames.contains(columnName)) { |
| ColumnAttributes ca = new ColumnAttributes(columnName, |
| (ValuesMode.CALCULATED == readRequest.getValuesMode() ? SequenceRepresentation.EXPLICIT |
| : mapValues.get("SequenceRepresentation").extract()), |
| new double[0], ((short) mapValues.get("IndependentFlag").extract() != 0), |
| mapValues.get("axistype").extract()); |
| |
| mapColumnAttributes.put(mapValues.get("Id").extract(), ca); |
| } |
| } |
| |
| if (ValuesMode.CALCULATED != readRequest.getValuesMode()) { |
| Query query2 = queryService.createQuery().select(idAttr, |
| localColumnEntityType.getAttribute("GenerationParameters")); |
| |
| for (Result result : query2.fetch(filter)) { |
| Map<String, Value> mapValues = result.getRecord(localColumnEntityType).getValues(); |
| |
| ColumnAttributes ca = mapColumnAttributes.get(mapValues.get("Id").extract()); |
| |
| if (ca != null) { |
| ca.setGenerationParameters(mapValues.get("GenerationParameters").extract()); |
| } |
| } |
| } |
| |
| for (Map.Entry<String, ColumnAttributes> me : mapColumnAttributes.entrySet()) { |
| ColumnAttributes ca = me.getValue(); |
| listColumnPairs.add(new ImmutablePair<Column, ColumnAttributes>(mapColumns.get(ca.getName()), ca)); |
| } |
| } |
| |
| return listColumnPairs; |
| } catch (AoException e) { |
| releaseColumns(listColumnPairs.stream().map(Pair::getLeft).toArray(Column[]::new)); |
| throw new DataAccessException("Unable to load column due to: " + e.reason, e); |
| } |
| } |
| |
| /** |
| * Returns the {@link ValueMatrix} CORBA service object associated with given |
| * {@link ReadRequest}. |
| * |
| * @param readRequest The {@code ReadRequest}. |
| * @return The {@code ValueMatrix} is returned. |
| * @throws AoException Thrown if unable to load the {@code ValueMatrix}. |
| */ |
| private ValueMatrix getValueMatrix(ReadRequest readRequest) throws AoException { |
| Entity entity = readRequest.getChannelGroup(); |
| T_LONGLONG iid = ODSConverter.toODSID(entity.getID()); |
| T_LONGLONG aid = ((ODSEntityType) modelManager.getEntityType(entity)).getODSID(); |
| ValueMatrixMode valueMatrixMode = ValueMatrixMode.CALCULATED; |
| switch (readRequest.getValuesMode()) { |
| case CALCULATED: |
| valueMatrixMode = ValueMatrixMode.CALCULATED; |
| break; |
| case STORAGE: |
| valueMatrixMode = ValueMatrixMode.STORAGE; |
| break; |
| default: |
| throw new DataAccessException( |
| String.format("Unsupported ValueMode %s!", readRequest.getValuesMode().name())); |
| } |
| |
| return modelManager.getApplElemAccess().getValueMatrixInMode(new ElemId(aid, iid), valueMatrixMode); |
| } |
| |
| /** |
| * Releases given {@link ValueMatrix} CORBA object. |
| * |
| * @param valueMatrix Will be released. |
| */ |
| private void releaseValueMatrix(ValueMatrix valueMatrix) { |
| if (valueMatrix == null) { |
| return; |
| } |
| |
| try { |
| valueMatrix.destroy(); |
| } catch (AoException aoe) { |
| // ignore |
| } finally { |
| valueMatrix._release(); |
| } |
| } |
| |
| /** |
| * Releases each CORBA {@link Column} object. |
| * |
| * @param columns Will be released. |
| */ |
| private void releaseColumns(Column[] columns) { |
| if (columns == null) { |
| return; |
| } |
| |
| for (Column column : columns) { |
| try { |
| column.destroy(); |
| } catch (AoException e) { |
| // ignore |
| } catch (Exception e) { |
| // ignore |
| } finally { |
| column._release(); |
| } |
| } |
| } |
| |
| } |