SequenceRepresentation etc. for LocalColumns.

Signed-off-by: Martin Fleischer <m.fleischer@peak-solution.de>
diff --git a/src/main/java/org/eclipse/mdm/api/odsadapter/ODSEntityManager.java b/src/main/java/org/eclipse/mdm/api/odsadapter/ODSEntityManager.java
index de1df72..de9ba71 100644
--- a/src/main/java/org/eclipse/mdm/api/odsadapter/ODSEntityManager.java
+++ b/src/main/java/org/eclipse/mdm/api/odsadapter/ODSEntityManager.java
@@ -359,7 +359,7 @@
 	 */
 	@Override
 	public List<MeasuredValues> readMeasuredValues(ReadRequest readRequest) throws DataAccessException {
-		return new ReadRequestHandler(odsModelManager).execute(readRequest);
+		return new ReadRequestHandler(odsModelManager, queryService).execute(readRequest);
 	}
 
 	/**
diff --git a/src/main/java/org/eclipse/mdm/api/odsadapter/ReadRequestHandler.java b/src/main/java/org/eclipse/mdm/api/odsadapter/ReadRequestHandler.java
index 4fe53f1..f4323be 100644
--- a/src/main/java/org/eclipse/mdm/api/odsadapter/ReadRequestHandler.java
+++ b/src/main/java/org/eclipse/mdm/api/odsadapter/ReadRequestHandler.java
@@ -15,26 +15,44 @@
 
 package org.eclipse.mdm.api.odsadapter;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map.Entry;
-
-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.massdata.ReadRequest;
-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.Unit;
-import org.eclipse.mdm.api.base.query.DataAccessException;
-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 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.
@@ -42,13 +60,56 @@
  * @since 1.0.0
  * @author Viktor Stoehr, Gigatronik Ingolstadt GmbH
  */
-public final class ReadRequestHandler {
+public final class ReadRequestHandler {

+	public static final class ColumnAttributes

+	{

+		private final String name;

+		private final SequenceRepresentation sequenceRepresentation;

+		private final double[] generationParameters;

+		private final boolean independent;

+		private final 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 SequenceRepresentation getSequenceRepresentation()

+		{

+			return sequenceRepresentation;

+		}

+		

+		public double[] getGenerationParameters()

+		{

+			return generationParameters;

+		}

+		

+		public boolean isIndependentColumn()

+		{

+			return independent;

+		}

+		

+		public AxisType getAxisType()

+		{

+			return axisType;

+		}

+	}
 
 	// ======================================================================
 	// Instance variables
 	// ======================================================================
-
-	private final ODSModelManager modelManager;
+

+	private final ODSModelManager modelManager;

+	private final QueryService queryService;
 
 	// ======================================================================
 	// Constructors
@@ -60,8 +121,9 @@
 	 * @param modelManager
 	 *            Used to gain access to value matrices.
 	 */
-	public ReadRequestHandler(ODSModelManager modelManager) {
-		this.modelManager = modelManager;
+	public ReadRequestHandler(ODSModelManager modelManager, QueryService queryService) {

+		this.modelManager = modelManager;

+		this.queryService = queryService;
 	}
 
 	// ======================================================================
@@ -80,18 +142,22 @@
 	 */
 	public List<MeasuredValues> execute(ReadRequest readRequest) throws DataAccessException {
 		ValueMatrix valueMatrix = null;
-		Column[] columns = null;
+		Column[] arrColumns = null;
 
 		try {
-			valueMatrix = getValueMatrix(readRequest);
-			columns = getODSColumns(readRequest, valueMatrix);
-			NameValueSeqUnit[] nvsus = valueMatrix.getValue(columns, readRequest.getStartIndex(),
+			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);
+			return ODSConverter.fromODSMeasuredValuesSeq(nvsus, arrColumnAttributes);
 		} catch (AoException aoe) {
 			throw new DataAccessException(aoe.reason, aoe);
 		} finally {
-			releaseColumns(columns);
+			releaseColumns(arrColumns);
 			releaseValueMatrix(valueMatrix);
 		}
 	}
@@ -116,38 +182,86 @@
 	 * @throws DataAccessException
 	 *             Thrown on wrong {@code ReadRequest} setup.
 	 */
-	private Column[] getODSColumns(ReadRequest readRequest, ValueMatrix valueMatrix)
-			throws AoException, DataAccessException {
-		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!
-			return valueMatrix.getColumns("*");
-		}
-
-		List<Column> columnList = new ArrayList<>();
-		try {
-			for (Entry<Channel, Unit> entry : readRequest.getChannels().entrySet()) {
-				Channel channel = entry.getKey();
-				Unit unit = entry.getValue();
-				Column[] columns = valueMatrix.getColumns(channel.getName());
-				if (columns == null || columns.length != 1) {
-					releaseColumns(columns);
-					throw new DataAccessException("Column with name '" + channel.getName() + "' not found.");
-				}
-				Column column = columns[0];
-				if (!unit.nameEquals(channel.getUnit().getName())) {
-					column.setUnit(unit.getName());
-				}
-				columnList.add(column);
-			}
-			return columnList.toArray(new Column[columnList.size()]);
-		} catch (AoException e) {
-			releaseColumns(columnList.toArray(new Column[columnList.size()]));
-			throw new DataAccessException("Unable to load column due to: " + e.reason, e);
-		}
+	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) {

+						mapColumns.put(column.getName(), 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("Column with name '" + channel.getName() + "' not found.");

+					}

+					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 nameAttr = localColumnEntityType.getAttribute("Name");

+				Attribute submatrixAttr = localColumnEntityType.getAttribute("SubMatrix");

+

+				Query query = queryService.createQuery()

+						.select(Lists.newArrayList(nameAttr,

+								localColumnEntityType.getAttribute("SequenceRepresentation"),

+								localColumnEntityType.getAttribute("GenerationParameters"),

+								localColumnEntityType.getAttribute("IndependentFlag"),

+								localColumnEntityType.getAttribute("axistype")));

+

+				Filter filter = Filter.and()

+//						.add(ComparisonOperator.IN_SET.create(nameAttr,

+//								mapColumns.keySet().stream().toArray(String[]::new)))

+						.add(ComparisonOperator.EQUAL.create(submatrixAttr, Long.valueOf(readRequest.getChannelGroup().getID())));

+				

+				Set<String> setColumnNames = mapColumns.keySet();

+

+				for (Result result : query.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()),

+								(ValuesMode.CALCULATED == readRequest.getValuesMode() ? new double[0] : mapValues.get("GenerationParameters").extract()),

+								((short) mapValues.get("IndependentFlag").extract() != 0),

+								mapValues.get("axistype").extract());

+

+						listColumnPairs.add(new ImmutablePair<Column, ColumnAttributes>(mapColumns.get(columnName), 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);

+		}

 	}
 
 	/**
@@ -163,8 +277,18 @@
 	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();
-		return modelManager.getApplElemAccess().getValueMatrixInMode(new ElemId(aid, iid), ValueMatrixMode.CALCULATED);
+		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);
 	}
 
 	/**
@@ -182,8 +306,10 @@
 			valueMatrix.destroy();
 		} catch (AoException aoe) {
 			// ignore
-		} finally {
-			valueMatrix._release();
+		} finally {

+			// After valueMatrix.destroy(), the CORBA object may have been removed from the POA and 

+			// thus be inaccessible. _release() often doesn't do anything in many ODS implementations, anyway.
+//			valueMatrix._release();
 		}
 	}
 
@@ -203,8 +329,10 @@
 				column.destroy();
 			} catch (AoException e) {
 				// ignore
-			} finally {
-				column._release();
+			} finally {

+				// After column.destroy(), the CORBA object may have been removed from the POA and 

+				// thus be inaccessible. _release() often doesn't do anything in many ODS implementations, anyway.
+//				column._release();
 			}
 		}
 	}
diff --git a/src/main/java/org/eclipse/mdm/api/odsadapter/transaction/WriteRequestHandler.java b/src/main/java/org/eclipse/mdm/api/odsadapter/transaction/WriteRequestHandler.java
index 34c8d2c..8a27cfd 100644
--- a/src/main/java/org/eclipse/mdm/api/odsadapter/transaction/WriteRequestHandler.java
+++ b/src/main/java/org/eclipse/mdm/api/odsadapter/transaction/WriteRequestHandler.java
@@ -127,7 +127,7 @@
 		Map<String, Value> values = core.getValues();
 		values.get(Entity.ATTR_NAME).set(writeRequest.getChannel().getName());
 		values.get(Entity.ATTR_MIMETYPE).set("application/x-asam.aolocalcolumn");
-		values.get(AE_LC_ATTR_INDEPENDENT).set(ODSConverter.toODSValidFlag(writeRequest.isIndependent()));
+		values.get(AE_LC_ATTR_INDEPENDENT).set((short) (writeRequest.isIndependent() ? 1 : 0));
 		values.get(AE_LC_ATTR_RAWDATATYPE).set(writeRequest.getRawScalarType());
 		values.get(AE_LC_ATTR_REPRESENTATION).set(writeRequest.getSequenceRepresentation());
 		values.get(AE_LC_ATTR_AXISTYPE).set(writeRequest.getAxisType());
@@ -137,26 +137,24 @@
 			ValueType<?> valueType = writeRequest.getRawScalarType().toValueType();
 			String unitName = writeRequest.getChannel().getUnit().getName();
 			values.put(AE_LC_ATTR_VALUES,
-					valueType.create(AE_LC_ATTR_VALUES, unitName, true, writeRequest.getValues()));
-
-			if (writeRequest.getSequenceRepresentation().isImplicit()) {
-				// PEAK ODS server: expects values written as generation
-				// parameters
-				Object genParamValues = writeRequest.getValues();
-				double[] genParamD = new double[Array.getLength(genParamValues)];
-				IntStream.range(0, genParamD.length)
-						.forEach(i -> genParamD[i] = ((Number) Array.get(genParamValues, i)).doubleValue());
-				values.get(AE_LC_ATTR_PARAMETERS).set(genParamD);
-			}
+					valueType.create(AE_LC_ATTR_VALUES, unitName, true, writeRequest.getValues()));

+			

+			// OpenATFX issue: For "implicit" columns, if no value for the GenerationParameters attribute is present,

+			// it is attempted to transfer the local column values (through which the generation parameters are

+			// available in these cases) to the GenerationParameters attribute without converting them to the 

+			// correct DS_DOUBLE data type first (unless it is a DOUBLE or LONG column), resulting in an exception.

+			// Hence, supply correctly converted generation parameters as a workaround:

+			if (writeRequest.getSequenceRepresentation().isImplicit()) {

+				Object genParamValues = writeRequest.getValues();

+				double[] genParamD = new double[Array.getLength(genParamValues)];

+				IntStream.range(0, genParamD.length)

+						.forEach(i -> genParamD[i] = ((Number) Array.get(genParamValues, i)).doubleValue());

+				values.get(AE_LC_ATTR_PARAMETERS).set(genParamD);

+			}

 
 			// flags
 			if (writeRequest.areAllValid()) {
 				values.get(AE_LC_ATTR_GLOBAL_FLAG).set((short) 15);
-				// PEAK ODS server issue: though global flag is true a flags
-				// array is expected
-				short[] flags = new short[Array.getLength(writeRequest.getValues())];
-				Arrays.fill(flags, (short) 15);
-				values.get(AE_LC_ATTR_FLAGS).set(flags);
 			} else {
 				short[] flags = ODSConverter.toODSValidFlagSeq(writeRequest.getFlags());
 				values.get(AE_LC_ATTR_FLAGS).set(flags);
diff --git a/src/main/java/org/eclipse/mdm/api/odsadapter/utils/ODSConverter.java b/src/main/java/org/eclipse/mdm/api/odsadapter/utils/ODSConverter.java
index ae893ca..25852d5 100644
--- a/src/main/java/org/eclipse/mdm/api/odsadapter/utils/ODSConverter.java
+++ b/src/main/java/org/eclipse/mdm/api/odsadapter/utils/ODSConverter.java
@@ -21,7 +21,8 @@
 import java.time.format.DateTimeFormatterBuilder;
 import java.time.format.DateTimeParseException;
 import java.time.temporal.ChronoField;
-import java.util.ArrayList;
+import java.util.ArrayList;

+import java.util.Arrays;

 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -49,7 +50,8 @@
 import org.eclipse.mdm.api.base.model.Value;
 import org.eclipse.mdm.api.base.model.ValueType;
 import org.eclipse.mdm.api.base.query.Aggregation;
-import org.eclipse.mdm.api.base.query.DataAccessException;
+import org.eclipse.mdm.api.base.query.DataAccessException;

+import org.eclipse.mdm.api.odsadapter.ReadRequestHandler;

 import org.eclipse.mdm.api.odsadapter.query.ODSAttribute;
 
 import com.google.common.collect.Sets;
@@ -652,12 +654,18 @@
 	 * @throws DataAccessException
 	 *             Thrown on conversion errors.
 	 */
-	public static List<MeasuredValues> fromODSMeasuredValuesSeq(NameValueSeqUnit[] odsMeasuredValuesSeq)
+	public static List<MeasuredValues> fromODSMeasuredValuesSeq(NameValueSeqUnit[] odsMeasuredValuesSeq, ReadRequestHandler.ColumnAttributes[] columnAttributesArray)
 			throws DataAccessException {
-		List<MeasuredValues> measuredValues = new ArrayList<>(odsMeasuredValuesSeq.length);
+		List<MeasuredValues> measuredValues = new ArrayList<>(odsMeasuredValuesSeq.length);

+		

+		Map<String, ReadRequestHandler.ColumnAttributes> mapColumnAttributes = new HashMap<>();

+		if (null != columnAttributesArray)

+		{

+			Arrays.stream(columnAttributesArray).forEach(ca -> mapColumnAttributes.put(ca.getName(), ca));

+		}
 
 		for (NameValueSeqUnit odsMeasuredValues : odsMeasuredValuesSeq) {
-			measuredValues.add(fromODSMeasuredValues(odsMeasuredValues));
+			measuredValues.add(fromODSMeasuredValues(odsMeasuredValues, mapColumnAttributes.get(odsMeasuredValues.valName)));
 		}
 
 		return measuredValues;
@@ -672,7 +680,7 @@
 	 * @throws DataAccessException
 	 *             Thrown on conversion errors.
 	 */
-	private static MeasuredValues fromODSMeasuredValues(NameValueSeqUnit odsMeasuredValues) throws DataAccessException {
+	private static MeasuredValues fromODSMeasuredValues(NameValueSeqUnit odsMeasuredValues, ReadRequestHandler.ColumnAttributes columnAttributes) throws DataAccessException {
 		TS_ValueSeq odsValueSeq = odsMeasuredValues.value;
 		DataType dataType = odsValueSeq.u.discriminator();
 		ScalarType scalarType;
@@ -720,9 +728,20 @@
 		} else {
 			throw new DataAccessException(
 					"Conversion for ODS measured points of type '" + dataType.toString() + "' does not exist.");
+		}

+		

+		if (null == columnAttributes)

+		{

+			columnAttributes = new ReadRequestHandler.ColumnAttributes("", null, new double[0], false, null);

 		}
 
-		return scalarType.createMeasuredValues(odsMeasuredValues.valName, odsMeasuredValues.unit, values,
+		return scalarType.createMeasuredValues(odsMeasuredValues.valName,

+				odsMeasuredValues.unit, 

+				columnAttributes.getSequenceRepresentation(),

+				columnAttributes.getGenerationParameters(),

+				columnAttributes.isIndependentColumn(),

+				columnAttributes.getAxisType(),

+				values,
 				fromODSValidFlagSeq(odsValueSeq.flag));
 	}