blob: bd6b2d0d4cb9aa1bcee98b5cb019de29e5d76d54 [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.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();
}
}
}
}