| /** |
| ******************************************************************************** |
| * Copyright (c) 2015-2021 Robert Bosch GmbH and others. |
| * |
| * This program and the accompanying materials are made |
| * available under the terms of the Eclipse Public License 2.0 |
| * which is available at https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Robert Bosch GmbH - initial API and implementation |
| ******************************************************************************** |
| */ |
| |
| package org.eclipse.app4mc.amalthea.model.util; |
| |
| import static org.eclipse.app4mc.amalthea.model.LabelAccessEnum.READ; |
| import static org.eclipse.app4mc.amalthea.model.LabelAccessEnum.WRITE; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Objects; |
| import java.util.Set; |
| import java.util.function.Function; |
| import java.util.stream.Collectors; |
| |
| import org.eclipse.app4mc.amalthea.model.ActivityGraphItem; |
| import org.eclipse.app4mc.amalthea.model.AmaltheaServices; |
| import org.eclipse.app4mc.amalthea.model.ClearEvent; |
| import org.eclipse.app4mc.amalthea.model.Condition; |
| import org.eclipse.app4mc.amalthea.model.ConditionConjunction; |
| import org.eclipse.app4mc.amalthea.model.ConditionDisjunction; |
| import org.eclipse.app4mc.amalthea.model.ConditionDisjunctionEntry; |
| import org.eclipse.app4mc.amalthea.model.EnumMode; |
| import org.eclipse.app4mc.amalthea.model.ExecutionNeed; |
| import org.eclipse.app4mc.amalthea.model.Group; |
| import org.eclipse.app4mc.amalthea.model.IActivityGraphItemContainer; |
| import org.eclipse.app4mc.amalthea.model.IExecutable; |
| import org.eclipse.app4mc.amalthea.model.Label; |
| import org.eclipse.app4mc.amalthea.model.LabelAccess; |
| import org.eclipse.app4mc.amalthea.model.LabelAccessEnum; |
| import org.eclipse.app4mc.amalthea.model.LabelAccessStatistic; |
| import org.eclipse.app4mc.amalthea.model.MinAvgMaxStatistic; |
| import org.eclipse.app4mc.amalthea.model.ModeLabel; |
| import org.eclipse.app4mc.amalthea.model.ModeLabelCondition; |
| import org.eclipse.app4mc.amalthea.model.ModeLiteral; |
| import org.eclipse.app4mc.amalthea.model.ModeSwitch; |
| import org.eclipse.app4mc.amalthea.model.ModeSwitchEntry; |
| import org.eclipse.app4mc.amalthea.model.ModeValueCondition; |
| import org.eclipse.app4mc.amalthea.model.NumericMode; |
| import org.eclipse.app4mc.amalthea.model.NumericStatistic; |
| import org.eclipse.app4mc.amalthea.model.ProbabilitySwitch; |
| import org.eclipse.app4mc.amalthea.model.ProbabilitySwitchEntry; |
| import org.eclipse.app4mc.amalthea.model.Process; |
| import org.eclipse.app4mc.amalthea.model.RelationalOperator; |
| import org.eclipse.app4mc.amalthea.model.Runnable; |
| import org.eclipse.app4mc.amalthea.model.RunnableCall; |
| import org.eclipse.app4mc.amalthea.model.ServerCall; |
| import org.eclipse.app4mc.amalthea.model.SetEvent; |
| import org.eclipse.app4mc.amalthea.model.SingleValueStatistic; |
| import org.eclipse.app4mc.amalthea.model.Switch; |
| import org.eclipse.app4mc.amalthea.model.SwitchEntry; |
| import org.eclipse.app4mc.amalthea.model.Ticks; |
| import org.eclipse.app4mc.amalthea.model.WaitEvent; |
| import org.eclipse.app4mc.amalthea.model.WhileLoop; |
| import org.eclipse.app4mc.amalthea.model.util.RuntimeUtil.TimeType; |
| import org.eclipse.emf.common.util.BasicEList; |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.common.util.EMap; |
| import org.eclipse.jdt.annotation.NonNull; |
| |
| import com.google.common.primitives.Ints; |
| |
| public class SoftwareUtil { |
| |
| // Suppress default constructor |
| private SoftwareUtil() { |
| throw new IllegalStateException("Utility class"); |
| } |
| |
| /** |
| * Traverse the contained call graph items and collect all items. |
| * |
| * @param container call graph, group, ... |
| * @return |
| * List of ActivityGraphItems |
| */ |
| public static EList<ActivityGraphItem> collectActivityGraphItems(final IActivityGraphItemContainer container) { |
| return collectActivityGraphItems(container, null, ActivityGraphItem.class, null); |
| } |
| |
| /** |
| * Traverse the contained call graph items and collect all items. |
| * Collection can be restricted to specific modes. |
| * |
| * @param container call graph, group, ... |
| * @param modes (optional) - null works |
| * @return |
| * List of ActivityGraphItems |
| */ |
| public static EList<ActivityGraphItem> collectActivityGraphItems(final IActivityGraphItemContainer container, final EMap<ModeLabel, String> modes) { |
| return collectActivityGraphItems(container, modes, ActivityGraphItem.class, null); |
| } |
| |
| /** |
| * Traverse the contained call graph items and collect all items. |
| * Collection can be restricted to specific modes and filtered by a lambda expression. |
| * |
| * @param container call graph, group, ... |
| * @param modes (optional) - null works |
| * @param filter lambda expression (e.g. "a -> a instanceof LabelAccess") |
| * @return |
| * List of ActivityGraphItems |
| */ |
| public static EList<ActivityGraphItem> collectActivityGraphItems(final IActivityGraphItemContainer container, final EMap<ModeLabel, String> modes, |
| final Function<ActivityGraphItem, Boolean> filter) { |
| return collectActivityGraphItems(container, modes, ActivityGraphItem.class, filter); |
| } |
| |
| /** |
| * Traverse the runnable items graph of a runnable and collect all items. |
| * Collection can be restricted to specific modes and filtered by class. |
| * |
| * @param container call graph, group, ... |
| * @param modes (optional) - null works |
| * @param targetClass subclass of ActivityGraphItem that restricts the result |
| * @return |
| * List of T extends ActivityGraphItems |
| */ |
| public static <T extends ActivityGraphItem> EList<T> collectActivityGraphItems(final IActivityGraphItemContainer container, final EMap<ModeLabel, String> modes, |
| final Class<T> targetClass) { |
| return collectActivityGraphItems(container, modes, targetClass, null); |
| } |
| |
| /** |
| * Traverse the runnable items graph of a runnable and collect all items. |
| * Collection can be restricted to specific modes and filtered by class and lambda expression. |
| * |
| * @param container call graph, group, ... |
| * @param modes (optional) - null works |
| * @param targetClass subclass of ActivityGraphItem that restricts the result |
| * @param filter lambda expression (e.g. "a -> a instanceof LabelAccess") |
| * @return |
| * List of T extends ActivityGraphItems |
| */ |
| public static <T extends ActivityGraphItem> EList<T> collectActivityGraphItems(final IActivityGraphItemContainer container, final EMap<ModeLabel, String> modes, |
| final Class<T> targetClass, final Function<T, Boolean> filter) { |
| EList<T> itemList = new BasicEList<>(); |
| if (container != null) { |
| collectActivityGraphItems(container.getItems(), modes, targetClass, filter, itemList); |
| } |
| return itemList; |
| } |
| |
| |
| private static <T extends ActivityGraphItem> void collectActivityGraphItems(final EList<ActivityGraphItem> input, final EMap<ModeLabel, String> modes, |
| final Class<T> targetClass, final Function<T, Boolean> filter, final List<T> itemList) { |
| for (ActivityGraphItem item : input) { |
| if (item instanceof Group) { |
| // **** Group |
| collectActivityGraphItems(((Group) item).getItems(), modes, targetClass, filter, itemList); |
| } else if (item instanceof WhileLoop) { |
| // **** WhileLoop |
| collectWhileLoopItems((WhileLoop) item, modes, targetClass, filter, itemList); |
| } else if (item instanceof Switch) { |
| // **** Switch -> check conditions |
| collectSwitchItems((Switch) item, modes, targetClass, filter, itemList); |
| } else if (item instanceof ModeSwitch) { |
| // **** ModeSwitch -> check conditions |
| collectModeSwitchItems((ModeSwitch) item, modes, targetClass, filter, itemList); |
| } else if (item instanceof ProbabilitySwitch) { |
| // **** ProbabilitySwitch |
| for (ProbabilitySwitchEntry pse : ((ProbabilitySwitch) item).getEntries()) { |
| collectActivityGraphItems(pse.getItems(), modes, targetClass, filter, itemList); |
| } |
| } else if (targetClass.isInstance(item)) { |
| T castedItem = targetClass.cast(item); |
| if (filter == null || filter.apply(castedItem)) { |
| itemList.add(castedItem); |
| } |
| } |
| } |
| } |
| |
| private static <T extends ActivityGraphItem> void collectWhileLoopItems(final WhileLoop loop, final EMap<ModeLabel, String> modes, |
| final Class<T> targetClass, final Function<T, Boolean> filter, final List<T> itemList) { |
| collectActivityGraphItems(loop.getItems(), modes, targetClass, filter, itemList); |
| } |
| |
| private static <T extends ActivityGraphItem> void collectSwitchItems(final Switch genericSwitch, final EMap<ModeLabel, String> modes, |
| final Class<T> targetClass, final Function<T, Boolean> filter, final List<T> itemList) { |
| boolean includeDefault = true; |
| for (SwitchEntry se : genericSwitch.getEntries()) { |
| if (modes == null) { |
| collectActivityGraphItems(se.getItems(), modes, targetClass, filter, itemList); |
| } else if (conditionIsSatisfiedBy(se.getCondition(), modes)) { |
| collectActivityGraphItems(se.getItems(), modes, targetClass, filter, itemList); |
| includeDefault = false; |
| break; |
| } |
| } |
| if (includeDefault && genericSwitch.getDefaultEntry() != null) { |
| collectActivityGraphItems(genericSwitch.getDefaultEntry().getItems(), modes, targetClass, filter, itemList); |
| } |
| } |
| |
| private static <T extends ActivityGraphItem> void collectModeSwitchItems(final ModeSwitch modeSwitch, final EMap<ModeLabel, String> modes, |
| final Class<T> targetClass, final Function<T, Boolean> filter, final List<T> itemList) { |
| boolean includeDefault = true; |
| for (ModeSwitchEntry mse : modeSwitch.getEntries()) { |
| if (modes == null) { |
| collectActivityGraphItems(mse.getItems(), modes, targetClass, filter, itemList); |
| } else if (mse.getCondition().isSatisfiedBy(modes)) { |
| collectActivityGraphItems(mse.getItems(), modes, targetClass, filter, itemList); |
| includeDefault = false; |
| break; |
| } |
| } |
| if (includeDefault && modeSwitch.getDefaultEntry() != null) { |
| collectActivityGraphItems(modeSwitch.getDefaultEntry().getItems(), modes, targetClass, filter, itemList); |
| } |
| } |
| |
| public static boolean conditionIsSatisfiedBy(Object condition, EMap<ModeLabel, String> context) { |
| // no context defined => satisfied (no restriction) |
| if (context == null) return true; |
| |
| // OR condition |
| if (condition instanceof ConditionDisjunction) { |
| return conditionIsSatisfiedBy((ConditionDisjunction) condition, context); |
| } |
| |
| // AND condition |
| if (condition instanceof ConditionConjunction) { |
| return conditionIsSatisfiedBy((ConditionConjunction) condition, context); |
| } |
| |
| // Compare the value of a mode label with a given value |
| if (condition instanceof ModeValueCondition) { |
| return conditionIsSatisfiedBy((ModeValueCondition) condition, context); |
| } |
| |
| // Compare the values of two mode labels |
| if (condition instanceof ModeLabelCondition) { |
| return conditionIsSatisfiedBy((ModeLabelCondition) condition, context); |
| } |
| |
| // no restriction for other types |
| return true; |
| } |
| |
| public static boolean conditionIsSatisfiedBy(ConditionDisjunction condition, EMap<ModeLabel, String> context) { |
| // no context defined => satisfied (no restriction) |
| if (context == null) return true; |
| |
| // OR condition |
| for (ConditionDisjunctionEntry entry : condition.getEntries()) { |
| if (conditionIsSatisfiedBy(entry, context)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public static boolean conditionIsSatisfiedBy(ConditionConjunction condition, EMap<ModeLabel, String> context) { |
| // no context defined => satisfied (no restriction) |
| if (context == null) return true; |
| |
| // AND condition |
| for (Condition entry : condition.getEntries()) { |
| if (!conditionIsSatisfiedBy(entry, context)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| public static boolean conditionIsSatisfiedBy(ModeValueCondition condition, EMap<ModeLabel, String> context) { |
| // no context defined => satisfied (no restriction) |
| if (context == null) return true; |
| |
| // Compare the value of a mode label with a given value |
| |
| // check completeness of specified condition |
| ModeLabel label = condition.getLabel(); |
| RelationalOperator relation = condition.getRelation(); |
| if (label == null || relation == RelationalOperator._UNDEFINED_) { |
| return false; |
| } |
| |
| // no value specified in context => satisfied (no restriction) |
| String labelValue = context.get(label); |
| return (labelValue == null) |
| || isTrue(label, label, relation, labelValue, condition.getValue()); |
| } |
| |
| public static boolean conditionIsSatisfiedBy(ModeLabelCondition condition, EMap<ModeLabel, String> context) { |
| // no context defined => satisfied (no restriction) |
| if (context == null) return true; |
| |
| // Compare the values of two mode labels |
| |
| // check completeness of specified condition |
| ModeLabel label1 = condition.getLabel1(); |
| ModeLabel label2 = condition.getLabel2(); |
| RelationalOperator relation = condition.getRelation(); |
| if (label1 == null || label2 == null || relation == RelationalOperator._UNDEFINED_) { |
| return false; |
| } |
| |
| // no value specified in context => satisfied (no restriction) |
| String labelValue1 = context.get(label1); |
| String labelValue2 = context.get(label2); |
| return labelValue1 == null || labelValue2 == null |
| || isTrue(label1, label2, relation, labelValue1, labelValue2); |
| } |
| |
| private static boolean isTrue(ModeLabel label1, ModeLabel label2, RelationalOperator relation, |
| String labelValue1, String labelValue2) { |
| |
| // check availability of modes and compatibility of mode label types |
| if (label1 == null || label1.getMode() == null |
| || label2 == null || label2.getMode() == null |
| || label1.getMode() != label2.getMode()) { |
| return false; |
| } |
| |
| if (label1.getMode() instanceof EnumMode) { |
| ModeLiteral literal1 = ((EnumMode) label1.getMode()).getLiteral(labelValue1); |
| ModeLiteral literal2 = ((EnumMode) label2.getMode()).getLiteral(labelValue2); |
| |
| if (literal1 == null || literal2 == null) { |
| return false; |
| } |
| |
| switch (relation) { |
| case EQUAL: |
| return literal1 == literal2; |
| case NOT_EQUAL: |
| return literal1 != literal2; |
| default: |
| break; |
| } |
| } |
| |
| if (label1.getMode() instanceof NumericMode) { |
| Integer int1 = Ints.tryParse(labelValue1); |
| Integer int2 = Ints.tryParse(labelValue2); |
| |
| if (int1 == null || int2 == null) { |
| return false; |
| } |
| |
| switch (relation) { |
| case EQUAL: |
| return int1.equals(int2); |
| case NOT_EQUAL: |
| return ! int1.equals(int2); |
| case GREATER_THAN: |
| return int1 > int2; |
| case LESS_THAN: |
| return int1 < int2; |
| default: |
| break; |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Returns a set of labels accessed from the runnable |
| * @param runnable runnable |
| * @param modes (optional) - null works |
| * @return |
| * Set of Labels |
| */ |
| public static Set<Label> getAccessedLabelSet(Runnable runnable, EMap<ModeLabel, String> modes) { |
| return collectActivityGraphItems(runnable.getActivityGraph(), modes, LabelAccess.class) |
| .stream().map(LabelAccess::getData).filter(Objects::nonNull).collect(Collectors.toSet()); |
| } |
| |
| /** |
| * Returns a set of labels read by the runnable |
| * @param runnable runnable |
| * @param modes (optional) - null works |
| * @return |
| * Set of Labels |
| */ |
| public static Set<Label> getReadLabelSet(Runnable runnable, EMap<ModeLabel, String> modes) { |
| return collectActivityGraphItems(runnable.getActivityGraph(), modes, LabelAccess.class, (la -> la.getAccess() == READ)) |
| .stream().map(LabelAccess::getData).filter(Objects::nonNull).collect(Collectors.toSet()); |
| } |
| |
| /** |
| * Returns a set of labels written by the runnable |
| * @param runnable runnable |
| * @param modes (optional) - null works |
| * @return |
| * Set of Labels |
| */ |
| public static Set<Label> getWriteLabelSet(Runnable runnable, EMap<ModeLabel, String> modes) { |
| return collectActivityGraphItems(runnable.getActivityGraph(), modes, LabelAccess.class, (la -> la.getAccess() == WRITE)) |
| .stream().map(LabelAccess::getData).filter(Objects::nonNull).collect(Collectors.toSet()); |
| } |
| |
| /** |
| * Returns a list of all label accesses of the runnable |
| * @param runnable runnable |
| * @param modes (optional) - null works |
| * @return |
| * List of LabelAccesses |
| */ |
| public static List<LabelAccess> getLabelAccessList(Runnable runnable, EMap<ModeLabel, String> modes) { |
| return collectActivityGraphItems(runnable.getActivityGraph(), modes, LabelAccess.class); |
| } |
| |
| /** |
| * Returns a list of read label accesses of the runnable |
| * @param runnable runnable |
| * @param modes (optional) - null works |
| * @return |
| * List of LabelAccesses |
| */ |
| public static List<LabelAccess> getReadLabelAccessList(Runnable runnable, EMap<ModeLabel, String> modes) { |
| return collectActivityGraphItems(runnable.getActivityGraph(), modes, LabelAccess.class, (la -> la.getAccess() == READ)); |
| } |
| |
| /** |
| * Returns a list of write label accesses of the runnable |
| * @param runnable runnable |
| * @param modes (optional) - null works |
| * @return |
| * List of LabelAccesses |
| */ |
| public static List<LabelAccess> getWriteLabelAccessList(Runnable runnable, EMap<ModeLabel, String> modes) { |
| return collectActivityGraphItems(runnable.getActivityGraph(), modes, LabelAccess.class, (la -> la.getAccess() == WRITE)); |
| } |
| |
| /** |
| * Returns a map: labels accessed from runnable -> the corresponding label accesses |
| * @param runnable runnable |
| * @param modes (optional) - null works |
| * @return |
| * Map: Label -> List of LabelAccesses |
| */ |
| public static Map<Label, List<LabelAccess>> getLabelToLabelAccessMap(Runnable runnable, EMap<ModeLabel, String> modes) { |
| return collectActivityGraphItems(runnable.getActivityGraph(), modes, LabelAccess.class, (la -> la.getData() != null)) |
| .stream().collect(Collectors.groupingBy(LabelAccess::getData)); |
| } |
| |
| /** |
| * Returns a map: labels accessed by the runnable -> the corresponding LabelAccessStatistics |
| * @param runnable runnable |
| * @param modes (optional) - null works |
| * @return Map: |
| * Label -> List of LabelAccessStatistics |
| */ |
| public static Map<Label, List<LabelAccessStatistic>> getLabelAccessStatisticsMap(Runnable runnable, EMap<ModeLabel, String> modes) { |
| return collectActivityGraphItems(runnable.getActivityGraph(), modes, LabelAccess.class, |
| (la -> la.getData() != null && la.getStatistic() != null)) |
| .stream().collect(Collectors.groupingBy( |
| LabelAccess::getData, |
| Collectors.mapping(LabelAccess::getStatistic, Collectors.toList()) )); |
| } |
| |
| /** |
| * Returns a map: labels read by the runnable -> the corresponding LabelAccessStatistics |
| * @param runnable runnable |
| * @param modes (optional) - null works |
| * @return |
| * Map: Label -> List of LabelAccessStatistics |
| */ |
| public static Map<Label, List<LabelAccessStatistic>> getReadLabelAccessStatisticsMap(Runnable runnable, EMap<ModeLabel, String> modes) { |
| return collectActivityGraphItems(runnable.getActivityGraph(), modes, LabelAccess.class, |
| (la -> la.getData() != null && la.getStatistic() != null && la.getAccess() == READ)) |
| .stream().collect(Collectors.groupingBy( |
| LabelAccess::getData, |
| Collectors.mapping(LabelAccess::getStatistic, Collectors.toList()) )); |
| } |
| |
| /** |
| * Returns a map: labels written by the runnable -> the corresponding LabelAccessStatistics |
| * @param runnable runnable |
| * @param modes (optional) - null works |
| * @return |
| * Map: Label -> List of LabelAccessStatistics |
| */ |
| public static Map<Label, List<LabelAccessStatistic>> getWriteLabelAccessStatisticsMap(Runnable runnable, EMap<ModeLabel, String> modes) { |
| return collectActivityGraphItems(runnable.getActivityGraph(), modes, LabelAccess.class, |
| (la -> la.getData() != null && la.getStatistic() != null && la.getAccess() == WRITE)) |
| .stream().collect(Collectors.groupingBy( |
| LabelAccess::getData, |
| Collectors.mapping(LabelAccess::getStatistic, Collectors.toList()) )); |
| } |
| |
| /** |
| * Returns a set of labels accessed by the process |
| * @param process process (Task or ISR) |
| * @param modes (optional) - null works |
| * @return |
| * Set of Labels |
| */ |
| public static Set<Label> getAccessedLabelSet(Process process, EMap<ModeLabel, String> modes) { |
| HashSet<Label> result = new HashSet<>(); |
| for (Runnable r : getRunnableList(process, modes)) |
| result.addAll(getAccessedLabelSet(r, modes)); |
| return result; |
| } |
| |
| /** |
| * Returns a set of labels read by the process |
| * @param process process (Task or ISR) |
| * @param modes (optional) - null works |
| * @return |
| * Set of Labels |
| */ |
| public static Set<Label> getReadLabelSet(Process process, EMap<ModeLabel, String> modes) { |
| HashSet<Label> result = new HashSet<>(); |
| for (Runnable r : getRunnableList(process, modes)) |
| result.addAll(getReadLabelSet(r, modes)); |
| return result; |
| } |
| |
| /** |
| * Returns a set of labels written by the process |
| * @param process process (Task or ISR) |
| * @param modes (optional) - null works |
| * @return |
| * Set of Labels |
| */ |
| public static Set<Label> getWriteLabelSet(Process process, EMap<ModeLabel, String> modes) { |
| HashSet<Label> result = new HashSet<>(); |
| for (Runnable r : getRunnableList(process, modes)) |
| result.addAll(getWriteLabelSet(r, modes)); |
| return result; |
| } |
| |
| /** |
| * Returns a list of all label accesses of the process |
| * @param process process (Task or ISR) |
| * @param modes (optional) - null works |
| * @return |
| * List of LabelAccesses |
| */ |
| public static List<LabelAccess> getLabelAccessList(Process process, EMap<ModeLabel, String> modes) { |
| ArrayList<LabelAccess> result = new ArrayList<>(); |
| for (Runnable r : getRunnableList(process, modes)) |
| result.addAll(getLabelAccessList(r, modes)); |
| return result; |
| } |
| |
| /** |
| * Returns a map: accessed labels of the process -> the corresponding label accesses |
| * @param process process (Task or ISR) |
| * @param modes (optional) - null works |
| * @return |
| * Map: Label -> List of LabelAccess |
| */ |
| public static Map<Label, List<LabelAccess>> getLabelToLabelAccessMap(Process process, EMap<ModeLabel, String> modes) { |
| HashMap<Label, List<LabelAccess>> result = new HashMap<>(); |
| for (Runnable run : getRunnableList(process, modes)) { |
| Map<Label, List<LabelAccess>> labelToLabelAccessMap = SoftwareUtil.getLabelToLabelAccessMap(run, modes); |
| for(Entry<Label, List<LabelAccess>> entry : labelToLabelAccessMap.entrySet()) { |
| Label label = entry.getKey(); |
| List<LabelAccess> accesses = entry.getValue(); |
| |
| List<LabelAccess> accessList = result.computeIfAbsent(label, k -> new ArrayList<>()); |
| accessList.addAll(accesses); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Returns a map: labels accessed by the process -> the corresponding label access statistics |
| * @param process process (Task or ISR) |
| * @param modes (optional) - null works |
| * @return |
| * Map: Label -> List of LabelAccessStatistic |
| */ |
| public static Map<Label, List<LabelAccessStatistic>> getLabelAccessStatisticsMap(Process process, EMap<ModeLabel, String> modes) { |
| HashMap<Label, List<LabelAccessStatistic>> result = new HashMap<>(); |
| for (Runnable r : getRunnableList(process, modes)) { |
| Map<Label, List<LabelAccessStatistic>> labelToLabelAccessMap = SoftwareUtil.getLabelAccessStatisticsMap(r, modes); |
| for(Entry<Label, List<LabelAccessStatistic>> entry : labelToLabelAccessMap.entrySet()) { |
| Label label = entry.getKey(); |
| List<LabelAccessStatistic> statistics = entry.getValue(); |
| |
| List<LabelAccessStatistic> statisticsList = result.computeIfAbsent(label, k -> new ArrayList<>()); |
| statisticsList.addAll(statistics); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Returns a map: labels read the process -> the corresponding label access statistics |
| * @param process process (Task or ISR) |
| * @param modes (optional) - null works |
| * @return |
| * Map: Label -> List of LabelAccessStatistic |
| */ |
| public static Map<Label, List<LabelAccessStatistic>> getReadLabelAccessStatisticsMap(Process process, EMap<ModeLabel, String> modes) { |
| HashMap<Label, List<LabelAccessStatistic>> result = new HashMap<>(); |
| for (Runnable run : getRunnableList(process, modes)) { |
| Map<Label, List<LabelAccessStatistic>> readLabelAccessStatisticsMap = SoftwareUtil.getReadLabelAccessStatisticsMap(run, modes); |
| for(Entry<Label, List<LabelAccessStatistic>> entry : readLabelAccessStatisticsMap.entrySet()) { |
| Label label = entry.getKey(); |
| List<LabelAccessStatistic> statistics = entry.getValue(); |
| |
| List<LabelAccessStatistic> statisticsList = result.computeIfAbsent(label, k -> new ArrayList<>()); |
| statisticsList.addAll(statistics); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Returns a map: labels written by the process -> the corresponding label access statistics |
| * @param process process (Task or ISR) |
| * @param modes (optional) - null works |
| * @return |
| * Map: Label -> List of LabelAccessStatistic |
| */ |
| public static Map<Label, List<LabelAccessStatistic>> getWriteLabelAccessStatisticsMap(Process process, EMap<ModeLabel, String> modes) { |
| HashMap<Label, List<LabelAccessStatistic>> result = new HashMap<>(); |
| for (Runnable run : getRunnableList(process, modes)) { |
| Map<Label, List<LabelAccessStatistic>> writeLabelAcessStatisticsMap = SoftwareUtil.getWriteLabelAccessStatisticsMap(run, modes); |
| for(Entry<Label, List<LabelAccessStatistic>> entry : writeLabelAcessStatisticsMap.entrySet()) { |
| Label label = entry.getKey(); |
| List<LabelAccessStatistic> statistics = entry.getValue(); |
| |
| List<LabelAccessStatistic> statisticsList = result.computeIfAbsent(label, k -> new ArrayList<>()); |
| statisticsList.addAll(statistics); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Returns the reads from a process to a certain label. Evaluates the LabelAccessStatistic. |
| * Definition of TimeType is possible. Null value returns the average case. |
| */ |
| public static float getLabelReadCount(Label label, Process process, EMap<ModeLabel, String> modes, TimeType timeType) { |
| if (timeType == null) |
| timeType = TimeType.ACET; |
| |
| EList<@NonNull LabelAccess> accessList = collectActivityGraphItems( |
| process.getActivityGraph(), |
| modes, |
| LabelAccess.class, |
| (la -> la.getAccess() == READ && la.getData() == label)); |
| |
| return sumUpLabelAccesses(timeType, accessList); |
| } |
| |
| /** |
| * Returns the writes from a process to a certain label. Evaluates the LabelAccessStatistic. |
| * Definition of TimeType is possible. Null value returns the average case. |
| */ |
| public static float getLabelWriteCount(Label label, Process process, EMap<ModeLabel, String> modes, TimeType timeType) { |
| if (timeType == null) |
| timeType = TimeType.ACET; |
| |
| EList<@NonNull LabelAccess> accessList = collectActivityGraphItems( |
| process.getActivityGraph(), |
| modes, |
| LabelAccess.class, |
| (la -> la.getAccess() == WRITE && la.getData() == label)); |
| |
| return sumUpLabelAccesses(timeType, accessList); |
| } |
| |
| private static float sumUpLabelAccesses(TimeType timeType, EList<@NonNull LabelAccess> accessList) { |
| float accesses = 0f; |
| for (LabelAccess la : accessList) { |
| if (la.getStatistic() == null || la.getStatistic().getValue() == null) { |
| accesses += 1f; |
| continue; |
| } |
| |
| NumericStatistic statistic = la.getStatistic().getValue(); |
| if (statistic instanceof SingleValueStatistic) { |
| accesses += ((SingleValueStatistic) statistic).getValue(); |
| } else if (statistic instanceof MinAvgMaxStatistic) { |
| MinAvgMaxStatistic stat = (MinAvgMaxStatistic) statistic; |
| switch (timeType) { |
| case ACET: |
| accesses += stat.getAvg(); |
| break; |
| case BCET: |
| accesses += stat.getMin(); |
| break; |
| case WCET: |
| accesses += stat.getMax(); |
| break; |
| } |
| } |
| } |
| return accesses; |
| } |
| |
| /** |
| * Returns a list of runnables called by the process |
| * @param process process (Task or ISR) |
| * @param modes (optional) - null works |
| * @return |
| * List of Runnables |
| */ |
| public static List<Runnable> getRunnableList(Process process, EMap<ModeLabel, String> modes) { |
| return collectActivityGraphItems(process.getActivityGraph(), modes, RunnableCall.class) |
| .stream().map(RunnableCall::getRunnable).filter(Objects::nonNull).collect(Collectors.toList()); |
| } |
| |
| /** |
| * Returns a set of runnables called by the process - no duplicates |
| * @param process process (Task or ISR) |
| * @param modes (optional) - null works |
| * @return |
| * Set of runnables called by the process |
| */ |
| public static Set<Runnable> getRunnableSet(Process process, EMap<ModeLabel, String> modes) { |
| return new HashSet<>(getRunnableList(process, modes)); |
| } |
| |
| /** |
| * Returns the number of label accesses from a statistic. |
| * The accType defines if the minimum, maximum or average accesses are returned. |
| */ |
| public static float getLabelAccessCountFromStatistics(LabelAccess labelAcc, TimeType accType) { |
| if (labelAcc.getStatistic() == null) |
| return 1; |
| |
| float accesses = 1; |
| final NumericStatistic value = labelAcc.getStatistic().getValue(); |
| if (value instanceof SingleValueStatistic) { |
| accesses = ((SingleValueStatistic)value).getValue(); |
| } else if (value instanceof MinAvgMaxStatistic) { |
| switch(accType) { |
| case ACET: |
| accesses = ((MinAvgMaxStatistic)value).getAvg(); |
| break; |
| case BCET: |
| accesses = ((MinAvgMaxStatistic)value).getMin(); |
| break; |
| case WCET: |
| accesses = ((MinAvgMaxStatistic)value).getMax(); |
| break; |
| default: |
| accesses = ((MinAvgMaxStatistic)value).getAvg(); |
| } |
| } |
| return accesses; |
| } |
| |
| /** |
| * Returns a list of runnables reading the label |
| * @param label label |
| * @param modes (optional) - null works |
| * @return |
| * List of Runnables |
| */ |
| public static List<Runnable> getReaderListOfLabel(Label label, EMap<ModeLabel, String> modes) { |
| ArrayList<Runnable> result = new ArrayList<>(); |
| for (LabelAccess la : label.getLabelAccesses()) { |
| if (la.getAccess() == LabelAccessEnum.READ) { |
| Runnable run = AmaltheaServices.getContainerOfType(la, Runnable.class); |
| if (modes == null || modes.isEmpty() || (collectActivityGraphItems(run.getActivityGraph(), modes)).contains(la)) { |
| result.add(AmaltheaServices.getContainerOfType(la, Runnable.class)); |
| } |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Returns a set of runnables reading the label |
| * @param label label |
| * @param modes (optional) - null works |
| * @return |
| * Set of Labels |
| */ |
| public static Set<Runnable> getReadersSetOfLabel(Label label, EMap<ModeLabel, String> modes) { |
| return new HashSet<>(getReaderListOfLabel(label, modes)); |
| } |
| |
| /** |
| * Returns a list of runnables writing the label |
| * @param label label |
| * @param modes (optional) - null works |
| * @return |
| * List of Runnables |
| */ |
| public static List<Runnable> getWriterListOfLabel(Label label, EMap<ModeLabel, String> modes) { |
| ArrayList<Runnable> result = new ArrayList<>(); |
| for (LabelAccess la : label.getLabelAccesses()) { |
| if (la.getAccess().equals(LabelAccessEnum.WRITE)) { |
| Runnable run = AmaltheaServices.getContainerOfType(la, Runnable.class); |
| if (modes == null || modes.isEmpty() || (collectActivityGraphItems(run.getActivityGraph(), modes)).contains(la)) { |
| result.add(AmaltheaServices.getContainerOfType(la, Runnable.class)); |
| } |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Returns a set of runnables writing the label |
| * @param label label |
| * @param modes (optional) - null works |
| * @return |
| * Set of Runnables |
| */ |
| public static Set<Runnable> getWriterSetOfLabel(Label label, EMap<ModeLabel, String> modes) { |
| return new HashSet<>(getWriterListOfLabel(label, modes)); |
| } |
| |
| /** |
| * Collects a list of set events calls for a process |
| * @param process process (Task or ISR) |
| * @param modes (optional) - null works |
| * @return |
| * List of SetEvents |
| */ |
| public static List<SetEvent> collectSetEvents(Process process, EMap<ModeLabel, String> modes) { |
| return collectActivityGraphItems(process.getActivityGraph(), modes, SetEvent.class); |
| } |
| |
| /** |
| * Collects a list of clear event calls for a process |
| * @param process process (Task or ISR) |
| * @param modes (optional) - null works |
| * @return |
| * List of ClearEvents |
| */ |
| public static List<ClearEvent> collectClearEvents(Process process, EMap<ModeLabel, String> modes) { |
| return collectActivityGraphItems(process.getActivityGraph(), modes, ClearEvent.class); |
| } |
| |
| /** |
| * Collects a list of wait event calls for a process |
| * @param process process (Task or ISR) |
| * @param modes (optional) - null works |
| * @return |
| * List of WaitEvents |
| */ |
| public static List<WaitEvent> collectWaitEvents(Process process, EMap<ModeLabel, String> modes) { |
| return collectActivityGraphItems(process.getActivityGraph(), modes, WaitEvent.class); |
| } |
| |
| /** |
| * Collects a list of event calls (clear, set or wait) of a process |
| * @param process process (Task or ISR) |
| * @param modes (optional) - null works |
| * @return |
| * List of CallSequenceItems |
| */ |
| public static List<ActivityGraphItem> collectEventsOfProcess(Process process, EMap<ModeLabel, String> modes) { |
| return collectActivityGraphItems(process.getActivityGraph(), modes, |
| (call -> call instanceof ClearEvent || call instanceof SetEvent || call instanceof WaitEvent)); |
| } |
| |
| /** |
| * Returns a list of all exchanged labels that are written by the sender process and read by the receiver process |
| * @param sender process (Task or ISR) |
| * @param receiver process (Task or ISR) |
| * @param modes (optional) - null works |
| * @return |
| * List of Labels |
| */ |
| public static List<Label> getInterTaskCommunication(Process sender, Process receiver, EMap<ModeLabel, String> modes) { |
| ArrayList<Label> result = new ArrayList<>(); |
| result.addAll(getWriteLabelSet(sender, modes)); |
| result.retainAll(getReadLabelSet(receiver, modes)); |
| return result; |
| } |
| |
| /** |
| * Returns a list of processes calling the runnable |
| * @param runnable runnable |
| * @param modes (optional) - null works |
| * @return |
| * List of Processes |
| */ |
| public static List<Process> getCallingProcesses(Runnable runnable, EMap<ModeLabel, String> modes) { |
| ArrayList<Process> result = new ArrayList<>(); |
| for (RunnableCall callerRC : runnable.getRunnableCalls()) { |
| IExecutable caller = callerRC.getContainingExecutable(); // null if container is not of type IExecutable |
| if (!(caller instanceof Process)) continue; |
| |
| Process proc = (Process) caller; |
| if (modes != null && !modes.isEmpty()) { |
| for (RunnableCall rc : collectActivityGraphItems(caller.getActivityGraph(), modes, RunnableCall.class)) { |
| if (rc.getRunnable() == runnable) { |
| result.add(proc); |
| break; |
| } |
| } |
| } else { |
| result.add(proc); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Returns a list of runnables directly calling the runnable |
| * @param runnable runnable |
| * @param modes (optional) - null works |
| * @return |
| * List of Runnables |
| */ |
| public static List<Runnable> getCallingRunnables(Runnable runnable, EMap<ModeLabel, String> modes) { |
| ArrayList<Runnable> result = new ArrayList<>(); |
| for (RunnableCall callerRC : runnable.getRunnableCalls()) { |
| IExecutable caller = callerRC.getContainingExecutable(); // null if container is not of type IExecutable |
| if (!(caller instanceof Runnable)) continue; |
| |
| Runnable run = (Runnable) caller; |
| if (modes != null && !modes.isEmpty()) { |
| for (RunnableCall rc : collectActivityGraphItems(caller.getActivityGraph(), modes, RunnableCall.class)) { |
| if (rc.getRunnable() == runnable) { |
| result.add(run); |
| break; |
| } |
| } |
| } |
| else |
| result.add(run); |
| } |
| return result; |
| } |
| |
| /** |
| * Returns a list of runnables directly called by the runnable |
| * @param runnable runnable |
| * @param modes (optional) - null works |
| * @return |
| * List of Runnables |
| */ |
| public static List<Runnable> getCalledRunnables(Runnable runnable, EMap<ModeLabel, String> modes) { |
| return collectActivityGraphItems(runnable.getActivityGraph(), modes, RunnableCall.class) |
| .stream().map(RunnableCall::getRunnable).filter(Objects::nonNull).collect(Collectors.toList()); |
| } |
| |
| /** |
| * Returns a list of all execution needs for a given runnable |
| * @param runnable runnable |
| * @param modes (optional) - null works |
| * @return |
| * List of ExecutionNeeds |
| */ |
| public static List<ExecutionNeed> getExecutionNeeds(Runnable runnable, EMap<ModeLabel, String> modes) { |
| return collectActivityGraphItems(runnable.getActivityGraph(), modes, ExecutionNeed.class); |
| |
| // Should nested runnable calls also be included ? (potential cycles) |
| } |
| |
| /** |
| * Returns a list of all execution needs for a given process |
| * @param process process (Task or ISR) |
| * @param modes (optional) - null works |
| * @return |
| * List of ExecutionNeeds |
| */ |
| public static List<ExecutionNeed> getExecutionNeeds(Process process, EMap<ModeLabel, String> modes) { |
| List<ExecutionNeed> result = new ArrayList<>(); |
| // add needs on process level |
| result.addAll(collectActivityGraphItems(process.getActivityGraph(), modes, ExecutionNeed.class)); |
| // add needs of called runnables |
| for (Runnable run : getRunnableList(process, modes)) { |
| result.addAll(getExecutionNeeds(run, modes)); |
| } |
| return result; |
| } |
| |
| /** |
| * Returns a list of all ticks for a given runnable |
| * @param runnable runnable |
| * @param modes (optional) - null works |
| * @return |
| * List of Ticks |
| */ |
| public static List<Ticks> getTicks(Runnable runnable, EMap<ModeLabel, String> modes) { |
| return collectActivityGraphItems(runnable.getActivityGraph(), modes, Ticks.class); |
| |
| // Should nested runnable calls also be included ? (potential cycles) |
| } |
| |
| /** |
| * Returns a list of all ticks for a given process |
| * @param process process (Task or ISR) |
| * @param modes (optional) - null works |
| * @return |
| * List of Ticks |
| */ |
| public static List<Ticks> getTicks(Process process, EMap<ModeLabel, String> modes) { |
| List<Ticks> result = new ArrayList<>(); |
| // add ticks on process level |
| result.addAll(collectActivityGraphItems(process.getActivityGraph(), modes, Ticks.class)); |
| // add ticks of called runnables |
| for(Runnable run : getRunnableList(process, modes)) { |
| result.addAll(getTicks(run, modes)); |
| } |
| return result; |
| } |
| |
| /** |
| * Returns a set of server calls for a runnable |
| * @param runnable runnable |
| * @param modes (optional) - null works |
| * @return |
| * Set of ServerCall |
| */ |
| public static Set<ServerCall> getServerCallSet(Runnable runnable, EMap<ModeLabel, String> modes) { |
| return collectActivityGraphItems(runnable.getActivityGraph(), modes, ServerCall.class).stream().collect(Collectors.toSet()); |
| } |
| |
| /** |
| * Returns a set of server calls for a process |
| * @param process process (Task or ISR) |
| * @param modes (optional) - null works |
| * @return |
| * Set of ServerCall |
| */ |
| public static Set<ServerCall> getServerCallSet(Process process, EMap<ModeLabel, String> modes) { |
| HashSet<ServerCall> result = new HashSet<>(); |
| for (Runnable run : getRunnableList(process, modes)) { |
| result.addAll(getServerCallSet(run, modes)); |
| } |
| return result; |
| } |
| } |