| /******************************************************************************** |
| * Copyright (c) 2015-2018 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.base.model; |
| |
| import static java.util.stream.IntStream.range; |
| import static org.eclipse.mdm.api.base.model.Value.readAt; |
| |
| import java.lang.reflect.Array; |
| import java.util.Arrays; |
| import java.util.Iterator; |
| import java.util.NoSuchElementException; |
| import java.util.stream.Collectors; |
| |
| /** |
| * Class represents a sequence of measured values. |
| * |
| * @since 1.0.0 |
| * @author Viktor Stoehr, Gigatronik Ingolstadt GmbH |
| * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH |
| */ |
| public final class MeasuredValues { |
| |
| // ====================================================================== |
| // Instance variables |
| // ====================================================================== |
| |
| private final ScalarType scalarType; |
| private final Object values; |
| private final boolean[] flags; |
| |
| /* |
| * TODO: - replace name and unit with the corresponding Channel & Unit |
| * entities - provide further information if required |
| * - provide an overall offset for this value sequence |
| */ |
| |
| private final String name; |
| private final String unit; |
| |
| private final int length; |
| |
| private final SequenceRepresentation sequenceRepresentation; |
| private final double[] generationParameters; |
| private final boolean independent; |
| private final AxisType axisType; |
| |
| // ====================================================================== |
| // Constructors |
| // ====================================================================== |
| |
| /** |
| * Constructor. |
| * |
| * @param scalarType |
| * The {@link ScalarType} of this measured values. |
| * @param name |
| * This is the name of the corresponding {@link Channel}. |
| * @param unit |
| * Name of the unit the contained values are of. |
| * @param sequenceRepresentation |
| * The {@link SequenceRepresentation} of the measured values. |
| * @param generationParameters |
| * The generation parameters for the measured values. |
| * @param independent |
| * The independent flag of the measured values. |
| * @param axisType |
| * The {@link AxisType} of the measured values. |
| * @param values |
| * The measured values. |
| * @param flags |
| * The validity flags of the measured values. |
| * @throws IllegalArgumentException |
| * Thrown if values or flags is null or length of values and |
| * flags is not equal. |
| */ |
| MeasuredValues(ScalarType scalarType, String name, String unit, SequenceRepresentation sequenceRepresentation, double[] generationParameters, boolean independent, AxisType axisType, Object values, boolean[] flags) { |
| this.name = name; |
| this.unit = (unit == null ? "" : unit); |
| this.scalarType = scalarType; |
| this.sequenceRepresentation = sequenceRepresentation; |
| this.generationParameters = generationParameters; |
| this.independent = independent; |
| this.axisType = axisType; |
| this.values = values; |
| |
| if (values == null || flags == null) { |
| throw new IllegalArgumentException("Neither values nor flags is allowed to be null."); |
| } else if (Array.getLength(values) != flags.length) { |
| throw new IllegalArgumentException("Length of values and flags is not equal."); |
| } |
| |
| this.flags = Arrays.copyOf(flags, flags.length); |
| length = flags.length; |
| } |
| |
| // ====================================================================== |
| // Public methods |
| // ====================================================================== |
| |
| /** |
| * Returns the name of this measured values sequence. |
| * |
| * @return The name is returned. |
| */ |
| public String getName() { |
| return name; |
| } |
| |
| /** |
| * Returns the unit name for this measured values sequence. |
| * |
| * @return The unit name is returned. |
| */ |
| public String getUnit() { |
| return unit; |
| } |
| |
| /** |
| * Returns the {@link ScalarType} of this measured values sequence. |
| * |
| * @return The {@code ScalarType} is returned. |
| */ |
| public ScalarType getScalarType() { |
| return scalarType; |
| } |
| |
| /** |
| * Returns the number of values of this measured values sequence. |
| * |
| * @return The number of values is returned. |
| */ |
| public int getLength() { |
| return length; |
| } |
| |
| /** |
| * Returns the {@link SequenceRepresentation} of this measured values sequence. |
| * |
| * @return The {@code SequenceRepresentation} is returned. |
| */ |
| public SequenceRepresentation getSequenceRepresentation() { |
| return sequenceRepresentation; |
| } |
| |
| /** |
| * Returns the generation parameters for this measured values sequence. |
| * |
| * @return The generation parameters are returned. |
| */ |
| public double[] getGenerationParameters() { |
| return generationParameters; |
| } |
| |
| /** |
| * Returns the independent flag of this measured values sequence. |
| * |
| * @return The independent flag is returned. |
| */ |
| public boolean isIndependent() { |
| return independent; |
| } |
| |
| /** |
| * Returns the {@link AxisType} of this measured values sequence. |
| * |
| * @return The {@code AxisType} is returned. |
| */ |
| public AxisType getAxisType() { |
| return axisType; |
| } |
| |
| /** |
| * Returns a typed {@link ValueIterator}. Its usage is described below: |
| * |
| * <pre> |
| * // assume the measuredValues().getScalarType() == ScalarType.BYTE |
| * ValueIterator<Byte> valueIterator = measuredValues().iterator(); |
| * while (valueIterator.hasNext()) { |
| * boolean isCurrentValid = valueIterator.isValid(); |
| * Byte currentValue = valueIterator.next(); |
| * } |
| * </pre> |
| * |
| * @param <E> |
| * This type has to be derived from the {@link ScalarType} of |
| * this measured values. |
| * @return A typed {@code ValueIterator} is returned. |
| */ |
| public <E> ValueIterator<E> iterator() { |
| // TODO provide custom implementations for each type and typed |
| // nextType() methods |
| // idea: getScalarType().createIterator(values, flags); // <- package |
| // private |
| return new ValueIterator<E>() { |
| private int index = 0; |
| |
| @Override |
| public boolean hasNext() { |
| return index < length; |
| } |
| @Override |
| public boolean isValid() { |
| return flags[index]; |
| } |
| @Override |
| @SuppressWarnings("unchecked") |
| public E next() { |
| if (hasNext()) { |
| return (E) Array.get(values, index++); |
| } |
| throw new NoSuchElementException("Subsequent value is not available."); |
| } |
| }; |
| } |
| |
| /** |
| * Returns a human readable {@code String} representation of this measured |
| * values. If this sequence contains more than 10 values, only the first and |
| * last 5 values are written. If a value is marked as not valid, then 'XX' |
| * is written instead of its value. |
| * |
| * @return The {@code String} representation of this entity. |
| */ |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder("MeasuredValues(ChannelName = ").append(getName()); |
| sb.append(", ScalarType = ").append(getScalarType()); |
| |
| if (!getUnit().isEmpty()) { |
| sb.append(", Unit = ").append(getUnit()); |
| } |
| |
| sb.append(", Values = ["); |
| String notValidMarker = "XX"; |
| if (getLength() > 10) { |
| sb.append(range(0, 5).mapToObj(i -> flags[i] ? readAt(values, i) : notValidMarker) |
| .collect(Collectors.joining(", "))); |
| sb.append(", ..., "); |
| sb.append(range(length - 5, length).mapToObj(i -> flags[i] ? readAt(values, i) : notValidMarker) |
| .collect(Collectors.joining(", "))); |
| } else if (getLength() > 0) { |
| sb.append(range(0, length).mapToObj(i -> flags[i] ? readAt(values, i) : notValidMarker) |
| .collect(Collectors.joining(", "))); |
| } |
| |
| return sb.append("])").toString(); |
| } |
| |
| // ====================================================================== |
| // Inner Types |
| // ====================================================================== |
| |
| /** |
| * The measured values iterator. |
| * |
| * @param <E> |
| * Type of the returned values. |
| * @since 1.0.0 |
| * @author Viktor Stoehr, Gigatronik Ingolstadt GmbH |
| */ |
| public interface ValueIterator<E> extends Iterator<E> { |
| |
| /** |
| * Returns true if the current value is marked as valid. |
| * |
| * @return True if current value is valid. |
| */ |
| boolean isValid(); |
| |
| } |
| |
| } |