blob: ecb301e910511f8e3865191772fa464331e3724d [file] [log] [blame]
/**
********************************************************************************
* 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);
}
}