| /** |
| ******************************************************************************** |
| * Copyright (c) 2019-2020 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 com.google.common.base.Preconditions.checkArgument; |
| |
| import java.util.ArrayList; |
| import java.util.Deque; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Objects; |
| import java.util.Set; |
| import java.util.stream.Collectors; |
| |
| import org.eclipse.app4mc.amalthea.model.AbstractEventChain; |
| import org.eclipse.app4mc.amalthea.model.AbstractProcess; |
| import org.eclipse.app4mc.amalthea.model.Amalthea; |
| import org.eclipse.app4mc.amalthea.model.AmaltheaFactory; |
| import org.eclipse.app4mc.amalthea.model.AmaltheaIndex; |
| import org.eclipse.app4mc.amalthea.model.AmaltheaServices; |
| import org.eclipse.app4mc.amalthea.model.ConstraintsModel; |
| import org.eclipse.app4mc.amalthea.model.EventChainContainer; |
| import org.eclipse.app4mc.amalthea.model.LimitType; |
| import org.eclipse.app4mc.amalthea.model.ProcessRequirement; |
| import org.eclipse.app4mc.amalthea.model.Runnable; |
| import org.eclipse.app4mc.amalthea.model.RunnableRequirement; |
| import org.eclipse.app4mc.amalthea.model.SubEventChain; |
| import org.eclipse.app4mc.amalthea.model.Task; |
| import org.eclipse.app4mc.amalthea.model.Time; |
| import org.eclipse.app4mc.amalthea.model.TimeMetric; |
| import org.eclipse.app4mc.amalthea.model.TimeRequirementLimit; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.jdt.annotation.Nullable; |
| |
| public class ConstraintsUtil { |
| |
| // Suppress default constructor |
| private ConstraintsUtil() { |
| throw new IllegalStateException("Utility class"); |
| } |
| |
| private static final String ARG_NULL_MESSAGE = "Argument is null"; |
| |
| /** |
| * @param process |
| * @return |
| */ |
| public static @Nullable Time getDeadline(final @NonNull AbstractProcess process) { |
| checkArgument(process != null, ARG_NULL_MESSAGE); |
| |
| final ConstraintsModel constraintsModel = getConstraintsModel(process); |
| if (constraintsModel == null) return null; |
| |
| return getDeadline(process, constraintsModel); |
| } |
| |
| /** |
| * @param process |
| * @param constModel |
| * @return |
| */ |
| public static @Nullable Time getDeadline(final @NonNull AbstractProcess process, final @NonNull ConstraintsModel constModel) { |
| checkArgument(process != null, ARG_NULL_MESSAGE); |
| checkArgument(constModel != null, ARG_NULL_MESSAGE); |
| |
| List<Time> list = getDeadlineRequirements(process, constModel).stream() |
| .map(req -> ((TimeRequirementLimit) req.getLimit()).getLimitValue()) |
| .filter(Objects::nonNull) |
| .sorted(Time::compareTo) |
| .collect(Collectors.toList()); |
| if (list.isEmpty()) return null; |
| |
| return list.get(0); |
| } |
| |
| /** |
| * @param runnable |
| * @return |
| */ |
| public static @Nullable Time getDeadline(final @NonNull Runnable runnable) { |
| checkArgument(runnable != null, ARG_NULL_MESSAGE); |
| |
| final ConstraintsModel constraintsModel = getConstraintsModel(runnable); |
| if (constraintsModel == null) return null; |
| |
| return getDeadline(runnable, constraintsModel); |
| } |
| |
| /** |
| * @param runnable |
| * @param constModel |
| * @return |
| */ |
| public static @Nullable Time getDeadline(final @NonNull Runnable runnable, final @NonNull ConstraintsModel constModel) { |
| checkArgument(runnable != null, ARG_NULL_MESSAGE); |
| checkArgument(constModel != null, ARG_NULL_MESSAGE); |
| |
| List<Time> list = getDeadlineRequirements(runnable, constModel).stream() |
| .map(req -> ((TimeRequirementLimit) req.getLimit()).getLimitValue()) |
| .filter(Objects::nonNull) |
| .sorted(Time::compareTo) |
| .collect(Collectors.toList()); |
| if (list.isEmpty()) return null; |
| |
| return list.get(0); |
| } |
| |
| // ***** Deadline Requirements ***** |
| |
| /** |
| * @param container |
| * @param process |
| * @param deadline |
| */ |
| public static void addNewDeadlineRequirement(final @NonNull ConstraintsModel container, final @NonNull AbstractProcess process, final @NonNull Time deadline) { |
| checkArgument(container != null, ARG_NULL_MESSAGE); |
| checkArgument(process != null, ARG_NULL_MESSAGE); |
| checkArgument(deadline != null, ARG_NULL_MESSAGE); |
| |
| final TimeRequirementLimit limit = AmaltheaFactory.eINSTANCE.createTimeRequirementLimit(); |
| limit.setMetric(TimeMetric.RESPONSE_TIME); |
| limit.setLimitType(LimitType.UPPER_LIMIT); |
| limit.setLimitValue(deadline); |
| final ProcessRequirement req = AmaltheaFactory.eINSTANCE.createProcessRequirement(); |
| req.setName("Process deadline"); |
| req.setProcess(process); |
| req.setLimit(limit); |
| container.getRequirements().add(req); |
| } |
| |
| /** |
| * @param container |
| * @param runnable |
| * @param deadline |
| */ |
| public static void addNewDeadlineRequirement(final @NonNull ConstraintsModel container, final @NonNull Runnable runnable, final @NonNull Time deadline) { |
| checkArgument(container != null, ARG_NULL_MESSAGE); |
| checkArgument(runnable != null, ARG_NULL_MESSAGE); |
| checkArgument(deadline != null, ARG_NULL_MESSAGE); |
| |
| final TimeRequirementLimit limit = AmaltheaFactory.eINSTANCE.createTimeRequirementLimit(); |
| limit.setMetric(TimeMetric.RESPONSE_TIME); |
| limit.setLimitType(LimitType.UPPER_LIMIT); |
| limit.setLimitValue(deadline); |
| final RunnableRequirement req = AmaltheaFactory.eINSTANCE.createRunnableRequirement(); |
| req.setName("Runnable deadline"); |
| req.setRunnable(runnable); |
| req.setLimit(limit); |
| container.getRequirements().add(req); |
| } |
| |
| /** |
| * @param task |
| * @param deadline |
| */ |
| public static void updateDeadlineRequirement(final @NonNull Task task, final @NonNull Time deadline) { |
| checkArgument(task != null, ARG_NULL_MESSAGE); |
| checkArgument(deadline != null, ARG_NULL_MESSAGE); |
| |
| final ConstraintsModel constraintsModel = getConstraintsModel(task); |
| if (constraintsModel == null) return; |
| |
| updateDeadlineRequirement(task, deadline, constraintsModel); |
| } |
| |
| /** |
| * @param task |
| * @param deadline |
| * @param constModel |
| */ |
| public static void updateDeadlineRequirement(final @NonNull Task task, final @NonNull Time deadline, final @NonNull ConstraintsModel constModel) { |
| checkArgument(task != null, ARG_NULL_MESSAGE); |
| checkArgument(deadline != null, ARG_NULL_MESSAGE); |
| checkArgument(constModel != null, ARG_NULL_MESSAGE); |
| |
| final List<ProcessRequirement> requirements = getDeadlineRequirements(task, constModel); |
| |
| if (requirements.isEmpty()) { |
| // create deadline requirement |
| addNewDeadlineRequirement(constModel, task, deadline); |
| } else { |
| // modify first deadline requirement |
| ProcessRequirement first = requirements.remove(0); |
| ((TimeRequirementLimit) first.getLimit()).setLimitValue(deadline); |
| // delete the rest |
| AmaltheaIndex.deleteAll(requirements); |
| } |
| } |
| |
| /** |
| * @param runnable |
| * @param deadline |
| */ |
| public static void updateDeadlineRequirement(final @NonNull Runnable runnable, final @NonNull Time deadline) { |
| checkArgument(runnable != null, ARG_NULL_MESSAGE); |
| checkArgument(deadline != null, ARG_NULL_MESSAGE); |
| |
| final ConstraintsModel constraintsModel = getConstraintsModel(runnable); |
| if (constraintsModel == null) return; |
| |
| updateDeadlineRequirement(runnable, deadline, constraintsModel); |
| } |
| |
| /** |
| * @param runnable |
| * @param deadline |
| * @param constModel |
| */ |
| public static void updateDeadlineRequirement(final @NonNull Runnable runnable, final @NonNull Time deadline, final @NonNull ConstraintsModel constModel) { |
| checkArgument(runnable != null, ARG_NULL_MESSAGE); |
| checkArgument(deadline != null, ARG_NULL_MESSAGE); |
| checkArgument(constModel != null, ARG_NULL_MESSAGE); |
| |
| final List<RunnableRequirement> requirements = getDeadlineRequirements(runnable, constModel); |
| |
| if (requirements.isEmpty()) { |
| // create deadline requirement |
| addNewDeadlineRequirement(constModel, runnable, deadline); |
| } else { |
| // modify first deadline requirement |
| RunnableRequirement first = requirements.remove(0); |
| ((TimeRequirementLimit) first.getLimit()).setLimitValue(deadline); |
| // delete the rest |
| AmaltheaIndex.deleteAll(requirements); |
| } |
| } |
| |
| /** |
| * @param runnable |
| * @param constModel |
| * @return |
| */ |
| public static List<RunnableRequirement> getDeadlineRequirements(final @NonNull Runnable runnable, final @NonNull ConstraintsModel constModel) { |
| checkArgument(runnable != null, ARG_NULL_MESSAGE); |
| checkArgument(constModel != null, ARG_NULL_MESSAGE); |
| |
| return constModel.getRequirements().stream() |
| .filter(req -> req.getLimit().getLimitType() == LimitType.UPPER_LIMIT |
| && req instanceof RunnableRequirement |
| && ((RunnableRequirement) req).getRunnable() == runnable |
| && req.getLimit() instanceof TimeRequirementLimit |
| && ((TimeRequirementLimit) req.getLimit()).getMetric() == TimeMetric.RESPONSE_TIME) |
| .map(req -> (RunnableRequirement) req) |
| .collect(Collectors.toList()); |
| } |
| |
| /** |
| * @param process |
| * @param constModel |
| * @return |
| */ |
| public static List<ProcessRequirement> getDeadlineRequirements(final @NonNull AbstractProcess process, final @NonNull ConstraintsModel constModel) { |
| checkArgument(process != null, ARG_NULL_MESSAGE); |
| checkArgument(constModel != null, ARG_NULL_MESSAGE); |
| |
| return constModel.getRequirements().stream() |
| .filter(req -> req.getLimit().getLimitType() == LimitType.UPPER_LIMIT |
| && req instanceof ProcessRequirement |
| && ((ProcessRequirement) req).getProcess() == process |
| && req.getLimit() instanceof TimeRequirementLimit |
| && ((TimeRequirementLimit) req.getLimit()).getMetric() == TimeMetric.RESPONSE_TIME) |
| .map(req -> (ProcessRequirement) req) |
| .collect(Collectors.toList()); |
| } |
| |
| /** |
| * Retrieve all abstract event chains from the constraints model. |
| * This includes event chains and sub event chains (contained as items). |
| * @param constModel The constraints model from which we want to have all abstract event chains. |
| * @return All abstract event chains. |
| */ |
| public static List<AbstractEventChain> getAllAbstractEventChains(final @NonNull ConstraintsModel constModel) { |
| checkArgument(constModel != null, ARG_NULL_MESSAGE); |
| |
| final List<AbstractEventChain> allECs = new ArrayList<>(constModel.getEventChains()); |
| final Deque<AbstractEventChain> compositeECs = allECs.stream().filter(ec -> !ec.getItems().isEmpty()).collect(Collectors.toCollection(LinkedList::new)); |
| // search for arbitrarily deep nested sub event chains |
| while(!compositeECs.isEmpty()) { |
| final AbstractEventChain compositeEC = compositeECs.poll(); |
| final Set<SubEventChain> subChains = compositeEC.getItems().stream().filter(EventChainContainer.class::isInstance) |
| .map(EventChainContainer.class::cast).map(EventChainContainer::getEventChain).collect(Collectors.toSet()); |
| allECs.addAll(subChains); |
| subChains.stream().filter(sec -> !sec.getItems().isEmpty()).forEach(compositeECs::offer); |
| } |
| |
| return allECs; |
| } |
| |
| |
| @SuppressWarnings("unused") |
| private static ConstraintsModel getConstraintsModel(final @NonNull EObject object) { |
| checkArgument(object != null, ARG_NULL_MESSAGE); |
| |
| Amalthea modelRoot = AmaltheaServices.getContainerOfType(object, Amalthea.class); |
| if (modelRoot == null) return null; |
| |
| return ModelUtil.getOrCreateConstraintsModel(modelRoot); |
| } |
| |
| } |