blob: d06010057de60ebeea3014c021ada9bb0e200719 [file] [log] [blame]
/**
********************************************************************************
* Copyright (c) 2015-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 java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
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.ArrivalCurveStimulus;
import org.eclipse.app4mc.amalthea.model.CustomStimulus;
import org.eclipse.app4mc.amalthea.model.EventStimulus;
import org.eclipse.app4mc.amalthea.model.ExecutionNeed;
import org.eclipse.app4mc.amalthea.model.Frequency;
import org.eclipse.app4mc.amalthea.model.HwFeature;
import org.eclipse.app4mc.amalthea.model.HwFeatureCategory;
import org.eclipse.app4mc.amalthea.model.IDiscreteValueDeviation;
import org.eclipse.app4mc.amalthea.model.ITimeDeviation;
import org.eclipse.app4mc.amalthea.model.InterProcessStimulus;
import org.eclipse.app4mc.amalthea.model.InterProcessTrigger;
import org.eclipse.app4mc.amalthea.model.ModeLabel;
import org.eclipse.app4mc.amalthea.model.PeriodicStimulus;
import org.eclipse.app4mc.amalthea.model.PeriodicSyntheticStimulus;
import org.eclipse.app4mc.amalthea.model.Process;
import org.eclipse.app4mc.amalthea.model.ProcessingUnit;
import org.eclipse.app4mc.amalthea.model.RelativePeriodicStimulus;
import org.eclipse.app4mc.amalthea.model.Runnable;
import org.eclipse.app4mc.amalthea.model.RunnableCall;
import org.eclipse.app4mc.amalthea.model.SWModel;
import org.eclipse.app4mc.amalthea.model.SingleStimulus;
import org.eclipse.app4mc.amalthea.model.Stimulus;
import org.eclipse.app4mc.amalthea.model.Ticks;
import org.eclipse.app4mc.amalthea.model.Time;
import org.eclipse.app4mc.amalthea.model.TimeUnit;
import org.eclipse.app4mc.amalthea.model.VariableRateStimulus;
import org.eclipse.emf.common.util.EMap;
import org.eclipse.emf.ecore.EObject;
public class RuntimeUtil {
// Suppress default constructor
private RuntimeUtil() {
throw new IllegalStateException("Utility class");
}
public enum TimeType {
BCET, ACET, WCET
}
public enum PositionType {
FIRST, LAST
}
public enum AccessDirection {
READ, WRITE
}
/**
* Computes the execution time of a Process (Task or ISR) - unique mapping is required
*
* @param process task or isr
* @param modes (optional) - null works
* @param executionCase BCET, ACET, WCET
* @return execution time
*/
public static Time getExecutionTimeForProcess(Process process, EMap<ModeLabel, String> modes, TimeType executionCase) {
Time dummy = FactoryUtil.createTime();
if (process == null) return dummy;
Amalthea model = getContainingModel(process);
if (model == null) return dummy;
Set<ProcessingUnit> puSet = DeploymentUtil.getAssignedCoreForProcess(process);
if (puSet.size() != 1) {
System.out.println("Mapping unclear. Process mapped to " + puSet.size() + " Cores - Use PU specific method");
return dummy;
}
ProcessingUnit processingUnit = puSet.iterator().next();
return getExecutionTimeForProcess(process, processingUnit, modes, executionCase);
}
/**
* Computes the execution time of a Process (Task or ISR) on a given processing unit
*
* @param process task or isr
* @param processingUnit executing processing unit
* @param modes (optional) - null works
* @param executionCase BCET, ACET, WCET
* @return execution time
*/
public static Time getExecutionTimeForProcess(Process process, ProcessingUnit processingUnit, EMap<ModeLabel, String> modes, TimeType executionCase) {
Time result = FactoryUtil.createTime();
if (process == null || processingUnit == null) return result;
for (Runnable runnable : SoftwareUtil.getRunnableList(process, modes) ) {
result = result.add(getExecutionTimeForRunnable(runnable, processingUnit, modes, executionCase));
}
return result;
}
/**
* Computes the execution time of a Runnable on a given processing unit
*
* @param runnable runnable
* @param processingUnit executing processing unit
* @param modes (optional) - null works
* @param executionCase BCET, ACET, WCET
* @return execution time
*/
public static Time getExecutionTimeForRunnable(Runnable runnable, ProcessingUnit processingUnit, EMap<ModeLabel, String> modes, TimeType executionCase) {
Time result = FactoryUtil.createTime();
List<Ticks> tickList = SoftwareUtil.getTicks(runnable, modes);
for (Ticks tick : tickList) {
result = result.add(getExecutionTimeForTicks(tick, processingUnit, executionCase));
}
List<ExecutionNeed> executionNeedList = SoftwareUtil.getExecutionNeeds(runnable, modes);
if (!executionNeedList.isEmpty()) {
for (ExecutionNeed need : executionNeedList) {
result = result.add(getExecutionTimeForExecutionNeeds(need, processingUnit, executionCase));
}
}
return result;
}
// Simon 11.01.2019:
// Idea/Discussion: Why is TimeType needed here?
// If an extended tick is given, probably also a ITimeDeviation shall be returned?
// (I guess, this heavily depends on the use case)
// --> Second step would now then be ITimeDeviation + TimeType -> Time
//
/**
* Computes time for ticks on a given processing unit
*/
public static Time getExecutionTimeForTicks(Ticks ticks, ProcessingUnit processingUnit, TimeType executionCase) {
Time result = null;
if (ticks.getExtended().get(processingUnit.getDefinition()) != null) {
IDiscreteValueDeviation deviation = ticks.getExtended().get(processingUnit.getDefinition());
result = getExecutionTimeForTicksDeviation(deviation, processingUnit, executionCase);
}
else {
result = getExecutionTimeForTicksDeviation(ticks.getDefault(), processingUnit, executionCase);
}
return result;
}
/**
* Computes time for ticks on a given processing unit
*
* @param deviation ticks deviation
* @param processingUnit executing processing unit
* @param executionCase BCET, ACET, WCET
* @return execution time
*/
public static Time getExecutionTimeForTicksDeviation(IDiscreteValueDeviation deviation, ProcessingUnit processingUnit, TimeType executionCase) {
double ticks = 0d;
switch (executionCase) {
case BCET:
ticks = deviation.getLowerBound().doubleValue();
break;
case ACET:
ticks = deviation.getAverage();
break;
case WCET:
ticks = deviation.getUpperBound().doubleValue();
break;
}
return getExecutionTimeForCycles(ticks, processingUnit.getFrequencyDomain().getDefaultValue());
}
/**
* Computes time for execution needs on a given processing unit
*
* @param need execution need
* @param processingUnit executing processing unit
* @param executionCase BCET, ACET, WCET
* @return execution time
*/
public static Time getExecutionTimeForExecutionNeeds(ExecutionNeed need, ProcessingUnit processingUnit, TimeType executionCase) {
Time result = FactoryUtil.createTime();
if (need == null || processingUnit == null) return result;
Amalthea model = getContainingModel(processingUnit);
if (model == null) return result;
for (Entry<String, IDiscreteValueDeviation> needEntry : need.getNeeds()) {
String categoryName = needEntry.getKey();
if (categoryName == null) continue;
Set<HwFeatureCategory> categorySet = AmaltheaIndex.getElements(model, categoryName, HwFeatureCategory.class);
if (categorySet.size() == 1) {
result = getExecutionTimeForExecutionNeedEntry(needEntry.getValue(), categorySet.iterator().next(), processingUnit, executionCase);
}
else {
//should not happen since name is unique identifier
System.out.println("Mutliple Categories with the same name: " + categoryName);
}
}
return result;
}
/**
* Computes time for specific execution need entry on a given processing unit
*
* @param deviation needs deviation
* @param hwFeatureCategory corresponding feature category
* @param processingUnit executing processing unit
* @param executionCase BCET, ACET, WCET
* @return execution time
*/
public static Time getExecutionTimeForExecutionNeedEntry(IDiscreteValueDeviation deviation, HwFeatureCategory hwFeatureCategory, ProcessingUnit processingUnit, TimeType executionCase) {
double ticks = 0d;
switch (executionCase) {
case BCET:
ticks = deviation.getLowerBound().doubleValue();
break;
case ACET:
ticks = deviation.getAverage();
break;
case WCET:
ticks = deviation.getUpperBound().doubleValue();
break;
}
double scaleFactor = 0;
for (HwFeature feature : processingUnit.getDefinition().getFeatures()) {
if (feature.getContainingCategory().equals(hwFeatureCategory)) {
scaleFactor = feature.getValue();
}
}
ticks = ticks*scaleFactor;
return getExecutionTimeForCycles(ticks, processingUnit.getFrequencyDomain().getDefaultValue());
}
/**
* Computes time for a number of ticks with a given frequency
*
* @param ticks ticks / cycles
* @param frequency frequency (of an executing processing unit)
* @return execution time
*/
public static Time getExecutionTimeForCycles(double ticks, Frequency frequency) {
if (frequency == null) return null;
double cyclesPerSecond = AmaltheaServices.convertToHertz(frequency).doubleValue();
double factor = 1.0d / cyclesPerSecond;
Time oneSecond = FactoryUtil.createTime(1, TimeUnit.S);
Time t1 = oneSecond.multiply(ticks);
Time result = t1.multiply(factor);
return result.adjustUnit();
}
/**
* Computes ticks for a given time (in seconds) and frequency.
*
* @param executionTime execution time in seconds
* @param frequency frequency (of an executing processing unit)
* @return ticks / cycles
*/
public static double getTicksForExecutionTimeInSeconds(BigDecimal executionTime, Frequency frequency) {
if (executionTime == null || frequency == null) return Double.NaN;
BigDecimal cyclesPerSecond = AmaltheaServices.convertToHertz(frequency);
BigDecimal ticks = executionTime.multiply(cyclesPerSecond);
return ticks.doubleValue();
}
/**
* Computes ticks for a given time and frequency.
*
* @param executionTime execution time
* @param frequency frequency (of an executing processing unit)
* @return ticks / cycles
*/
public static double getTicksForExecutionTime(Time executionTime, Frequency frequency) {
if (executionTime == null) return Double.NaN;
return getTicksForExecutionTimeInSeconds(AmaltheaServices.convertToSeconds(executionTime), frequency);
}
/**
* Sets the runtime of the given runnable to the given ExecutionNeed
*/
public static void setRuntimeOfRunnable(Runnable runnable, ExecutionNeed need, EMap<ModeLabel, String> modes) {
clearRuntimeOfRunnable(runnable, modes);
addRuntimeToRunnable(runnable, need);
}
/**
* Sets the runtime of the given runnable to the given Ticks
*/
public static void setRuntimeOfRunnable(Runnable runnable, Ticks ticks, EMap<ModeLabel, String> modes) {
clearRuntimeOfRunnable(runnable, modes);
addRuntimeToRunnable(runnable, ticks);
}
/**
* Adds Runtime (given as ExecutionNeed) to an existing Runnable
* currently without consideration of modes
*/
public static void addRuntimeToRunnable(Runnable runnable, ExecutionNeed need) {
runnable.getRunnableItems().add(need);
}
/**
* Adds Runtime (given as Ticks) to an existing Runnable
* currently without consideration of modes
*/
public static void addRuntimeToRunnable(Runnable runnable, Ticks ticks) {
runnable.getRunnableItems().add(ticks);
}
/**
* Clears all runtime information (execution needs and ticks)
*
* @param model Amalthea model
* @param modes (optional) - null works
*/
public static void clearRuntimeOfModel(Amalthea model, EMap<ModeLabel, String> modes) {
List<Process> processes = new ArrayList<>();
processes.addAll(model.getSwModel().getTasks());
processes.addAll(model.getSwModel().getIsrs());
for (Process process : processes) {
clearRuntimeOfProcess(process, modes);
}
}
/**
* Clears all runtime information (execution needs and ticks)
*
* @param process task or isr
* @param modes (optional) - null works
*/
public static void clearRuntimeOfProcess(Process process, EMap<ModeLabel, String> modes) {
List<ExecutionNeed> executionNeeds = SoftwareUtil.getExecutionNeeds(process, modes);
AmaltheaIndex.deleteAll(executionNeeds);
List<Ticks> ticks = SoftwareUtil.getTicks(process, modes);
AmaltheaIndex.deleteAll(ticks);
}
/**
* Clears all runtime information (execution needs and ticks)
*
* @param runnable runnable
* @param modes (optional) - null works
*/
public static void clearRuntimeOfRunnable(Runnable runnable, EMap<ModeLabel, String> modes) {
List<ExecutionNeed> executionNeeds = SoftwareUtil.getExecutionNeeds(runnable, modes);
AmaltheaIndex.deleteAll(executionNeeds);
List<Ticks> ticks = SoftwareUtil.getTicks(runnable, modes);
AmaltheaIndex.deleteAll(ticks);
}
/**
* Creates a new Runnable with the given runtime at beginning / end of the given process
*
* @param process containing process (task or isr)
* @param need execution need
* @param runnableName name of new runnable
* @param positon FIRST, LAST
* @return the new runnable
*/
public static Runnable addRuntimeToProcessAsNewRunnable(Process process, ExecutionNeed need, String runnableName, PositionType positon) {
Runnable run = AmaltheaFactory.eINSTANCE.createRunnable();
run.setName(runnableName);
run.getRunnableItems().add(need);
RunnableCall call = AmaltheaFactory.eINSTANCE.createRunnableCall();
call.setRunnable(run);
if (positon.equals(PositionType.FIRST)) {
process.getActivityGraph().getItems().add(0, call);
} else {
process.getActivityGraph().getItems().add(call);
}
return run;
}
/**
* Creates a new Runnable with the given runtime at beginning / end of the given process
*
* @param process containing process (task or isr)
* @param ticks ticks
* @param runnableName name of new runnable
* @param positon FIRST, LAST
* @return the new runnable
*/
public static Runnable addRuntimeToProcessAsNewRunnable(Process process, Ticks ticks, String runnableName, PositionType positon) {
Runnable run = AmaltheaFactory.eINSTANCE.createRunnable();
run.setName(runnableName);
run.getRunnableItems().add(ticks);
RunnableCall call = AmaltheaFactory.eINSTANCE.createRunnableCall();
call.setRunnable(run);
if (positon.equals(PositionType.FIRST)) {
process.getActivityGraph().getItems().add(0, call);
} else {
process.getActivityGraph().getItems().add(call);
}
return run;
}
//#######################################################
/**
* Calculates the utilization for a given procUnit
*/
public static double getProcUnitUtilization(ProcessingUnit procUnit, Amalthea model, TimeType tt,
List<HwFeature> hwFeatures, EMap<ModeLabel, String> modes) {
double utilization = 0.0;
for (Process proc : DeploymentUtil.getProcessesMappedToCore(procUnit, model)) {
utilization += getProcessUtilization(proc, procUnit, model, tt, hwFeatures, modes);
}
return utilization;
}
/**
* Calculates the utilization for a given process
*
* @return Map: procUnit -&gt; utilization
*/
public static Map<ProcessingUnit, Double> getProcessUtilization(Process process, Amalthea model, TimeType tt,
List<HwFeature> hwFeatures, EMap<ModeLabel, String> modes) {
HashMap<ProcessingUnit, Double> utilizations = new HashMap<>();
Set<ProcessingUnit> procUnits = DeploymentUtil.getAssignedCoreForProcess(process);
for (ProcessingUnit procUnit : procUnits) {
double utilization = getProcessUtilization(process, procUnit, model, tt, hwFeatures, modes);
utilizations.put(procUnit, utilization);
}
return utilizations;
}
/**
* Calculates the utilization for a given process on a given procUnit
* <p>
* Assumption (wrong): All triggers activate the process on all procUnits together! (at the same time)
*
* @return utilization
*/
public static double getProcessUtilization(Process process, ProcessingUnit procUnit, Amalthea model, TimeType tt,
List<HwFeature> hwFeatures, EMap<ModeLabel, String> modes) {
double utilization = 0.0;
List<Time> periods = getPeriodsOfProcess(process, tt, modes);
Time time = getExecutionTimeForProcess(process, modes, tt);
if (time.getValue().compareTo(BigInteger.valueOf(0)) < 0) {
System.err.println("execTime " + time);
}
for (Time period : periods) {
// System.out.println(process.getName() + " Period: " + TimeUtil.timeToString(period) + " ET: "+TimeUtil.timeToString(time));
if (period != null && period.getValue().intValue() != 0) {
utilization += time.divide(period);
}
}
return utilization;
}
/**
* Calculates the process utilization
*/
public static double getProcessUtilization(Process process, Time period, TimeType tt, EMap<ModeLabel, String> modes) {
Time time = getExecutionTimeForProcess(process, modes, tt);
return time.divide(period);
}
/**
* Returns the cumulative process utilization, i.e. runtime on every procUnit summed up
*
* @return Map: process -&gt; sum of utilization on all procUnits
*/
public static Map<Process, Double> getCumulativeProcessUtilizations(Amalthea model, TimeType tt,
List<HwFeature> hwFeatures, EMap<ModeLabel, String> modes) {
HashMap<Process, Double> utilizations = new HashMap<>();
List<Process> procList = new ArrayList<>();
procList.addAll(model.getSwModel().getTasks());
procList.addAll(model.getSwModel().getIsrs());
for (Process proc : procList) {
Map<ProcessingUnit, Double> utilizationMap = getProcessUtilization(proc, model, tt, hwFeatures, modes);
double util = 0.0;
for (Double value : utilizationMap.values()) {
util += value;
}
utilizations.put(proc, util);
}
return utilizations;
}
/**
* Gets all Period ranges from the model
*/
public static Map<Process, List<Time>> getPeriodsOfAllProcesses(Amalthea model, TimeType tt,
EMap<ModeLabel, String> modes) {
Map<Process, List<Time>> result = new HashMap<>();
List<Process> processes = new ArrayList<>();
processes.addAll(model.getSwModel().getTasks());
processes.addAll(model.getSwModel().getIsrs());
for (Process process : processes) {
List<Time> periods = getPeriodsOfProcess(process, tt, modes);
result.put(process, periods);
}
return result;
}
/**
* Returns a list of all triggering periods. Sorted by shortest period first!
*/
public static List<Time> getPeriodsOfProcess(Process process, TimeType tt, EMap<ModeLabel, String> modes) {
List<Time> result = new ArrayList<>();
// System.out.println(process.getName());
for (Stimulus stimulus : process.getStimuli()) {
// System.out.println(" Stimulus "+stimulus);
if (stimulus instanceof PeriodicStimulus) {
PeriodicStimulus p = ((PeriodicStimulus) stimulus);
result.add(p.getRecurrence());
} else if (stimulus instanceof InterProcessStimulus) {
InterProcessStimulus ip = ((InterProcessStimulus) stimulus);
long ipPrescaler = 1L;
if (ip.getCounter() != null) {
ipPrescaler = ip.getCounter().getPrescaler();
}
// System.out.println(" IP found");
Map<Process, Long> triggeringProcesses = getTriggeringProcesses(ip, modes);
if (triggeringProcesses.containsKey(process)) {
// TODO decided what to do with self triggering processes
triggeringProcesses.remove(process);
}
for (Entry<Process, Long> entry : triggeringProcesses.entrySet()) {
Process triggeringProcess = entry.getKey();
// System.out.println(" --triggered by " + triggeringProcess.getName());
// InterProcessActivation ipa = getIpaForStimulus(stimulus, triggeringProcess, modes);
long ipaPrescaler = entry.getValue();
List<Time> periods = getPeriodsOfProcess(triggeringProcess, tt, modes);
for (Time t : periods) {
// if(ipa.getCounter() != null) {
// ipaPrescaler = ipa.getCounter().getPrescaler();
// }
result.add(t.multiply(ipaPrescaler * ipPrescaler));
}
}
// System.out.println("InterProcess eContainer: "+ip.eContainer());
// System.out.println(ip);
// System.out.println(getTriggeringProcess(model, ip));
// result.addAll(getPeriods(model, getTriggeringProcess(model, ip), tt));
} else if (stimulus instanceof RelativePeriodicStimulus) {
RelativePeriodicStimulus rp = ((RelativePeriodicStimulus) stimulus);
Time time = null;
switch (tt) {
case ACET:
// TODO check changes
if (rp.getNextOccurrence() != null) {
time = rp.getNextOccurrence().getAverage();
}
break;
case BCET:
if (rp.getNextOccurrence() != null) {
time = (rp.getNextOccurrence().getUpperBound());
}
break;
case WCET:
if (rp.getNextOccurrence() != null) {
time = (rp.getNextOccurrence().getLowerBound());
}
break;
default:
break;
}
if (time != null) {
result.add(time);
}
}
}
Collections.sort(result);
return result;
}
/**
* Returns all Processes that trigger the given InterProcessStimulus
*
* @return Map: process -&gt; prescaler value
*/
public static Map<Process, Long> getTriggeringProcesses(InterProcessStimulus ip, EMap<ModeLabel, String> modes) {
Map<Process, Long> result = new HashMap<>();
for (InterProcessTrigger interProcessTrigger : ip.getExplicitTriggers()) {
Process parentProcess = AmaltheaServices.getContainerOfType(interProcessTrigger, Process.class);
if (interProcessTrigger.getCounter() != null) {
result.put(parentProcess, interProcessTrigger.getCounter().getPrescaler());
} else {
result.put(parentProcess, 1L);
}
}
return result;
}
/**
* Returns a map of all stimuli, triggered by this process, associated with its prescaler.
*/
public static Map<Stimulus, Long> getTriggeredStimuli(Process process, EMap<ModeLabel, String> modes) {
HashMap<Stimulus, Long> stimuliMap = new HashMap<>();
List<InterProcessTrigger> interProcessTriggers = SoftwareUtil
.collectActivityGraphItems(process.getActivityGraph(), modes, InterProcessTrigger.class);
interProcessTriggers.forEach(ipa -> {
if (ipa.getCounter() != null) {
stimuliMap.put(ipa.getStimulus(), ipa.getCounter().getPrescaler());
} else {
stimuliMap.put(ipa.getStimulus(), 1L);
}
});
return stimuliMap;
}
/**
* Returns a Map of all sporadically triggered processes with their prescaler
*
* @return Map of processes with a sporadic activation and depending on tt the
* time between activations
*/
public static Map<Process, List<Time>> getProcessesWithRelativePeriodicStimulus(Amalthea model, TimeType tt) {
Set<Process> processes = new HashSet<>();
processes.addAll(model.getSwModel().getTasks());
processes.addAll(model.getSwModel().getIsrs());
// TODO check
Map<Process, List<Time>> result = processes.stream().collect(Collectors.toMap(p -> p,
p -> p.getStimuli().stream()
.filter(RelativePeriodicStimulus.class::isInstance)
.map(s -> getActivationTimeFromDeviation(((RelativePeriodicStimulus) s).getNextOccurrence(), tt))
.collect(Collectors.toList())));
return result;
}
private static Time getActivationTimeFromDeviation(ITimeDeviation deviation, TimeType tt) {
switch (tt) {
case ACET:
return deviation.getAverage();
case BCET:
return deviation.getUpperBound();
case WCET:
return deviation.getLowerBound();
}
return null;
}
/**
* Returns a Map of all sporadically triggered processes with their prescaler
*
* @return Map of processes with a sporadic activation and the deviation of the
* activations
*/
public static Map<Process, List<ITimeDeviation>> getProcessesWithRelativePeriodicStimulus(Amalthea model) {
List<Process> processes = new ArrayList<>();
processes.addAll(model.getSwModel().getTasks());
processes.addAll(model.getSwModel().getIsrs());
// TODO check
Map<Process, List<ITimeDeviation>> result = processes.stream()
.filter(p -> p.getStimuli().stream()
.filter(RelativePeriodicStimulus.class::isInstance)
.collect(Collectors.toList())
.size() > 0)
.collect(Collectors.toMap(p -> p,
p -> p.getStimuli().stream()
.filter(RelativePeriodicStimulus.class::isInstance)
.map(s -> ((RelativePeriodicStimulus) s).getNextOccurrence())
.collect(Collectors.toList())));
return result;
}
/**
* Returns a map from process to all stimuli (matching filter) that
* lead to that process with the correct prescaler
*/
public static Map<Process, Map<Stimulus, Long>> getPlainTriggersForModel(Amalthea model, Function<Stimulus, Boolean> filter) {
HashMap<Process, Map<Stimulus, Long>> map = new HashMap<>();
List<Process> processes = new ArrayList<>();
SWModel swModel = ModelUtil.getOrCreateSwModel(model);
processes.addAll(swModel.getTasks());
processes.addAll(swModel.getIsrs());
for (Process p : processes) {
Map<Stimulus, Long> plainTriggerForProcess = getPlainTriggerForProcess(p, 1, filter);
if (plainTriggerForProcess != null && !plainTriggerForProcess.isEmpty()) {
map.put(p, plainTriggerForProcess);
}
}
return map;
}
/**
* Returns all stimuli (matching filter) that trigger the given process
*/
public static Map<Stimulus, Long> getPlainTriggerForProcess(Process process, long depthCounter, Function<Stimulus, Boolean> filter) {
HashMap<Stimulus, Long> map = new HashMap<>();
for (Stimulus stimulus : process.getStimuli()) {
if (filter == null || Boolean.TRUE.equals(filter.apply(stimulus)) ) {
if (stimulus instanceof PeriodicStimulus) {
PeriodicStimulus p = ((PeriodicStimulus) stimulus);
map.put(p, depthCounter);
} else if (stimulus instanceof ArrivalCurveStimulus) {
ArrivalCurveStimulus p = ((ArrivalCurveStimulus) stimulus);
map.put(p, depthCounter);
} else if (stimulus instanceof CustomStimulus) {
CustomStimulus p = ((CustomStimulus) stimulus);
map.put(p, depthCounter);
} else if (stimulus instanceof EventStimulus) {
EventStimulus p = ((EventStimulus) stimulus);
map.put(p, depthCounter);
} else if (stimulus instanceof InterProcessStimulus) {
InterProcessStimulus ip = ((InterProcessStimulus) stimulus);
long ipPrescaler = 1L;
if (ip.getCounter() != null) {
ipPrescaler = ip.getCounter().getPrescaler();
}
Map<Process, Long> triggeringProcesses = getTriggeringProcesses(ip, null);
for (Entry<Process, Long> entry : triggeringProcesses.entrySet()) {
Process triggeringProcess = entry.getKey();
if (triggeringProcess.equals(process)) {
continue; // don't follow loops
}
long ipaPrescaler = entry.getValue();
Map<Stimulus, Long> plainTriggerForProcess = getPlainTriggerForProcess(triggeringProcess, depthCounter * ipaPrescaler * ipPrescaler,
filter);
map.putAll(plainTriggerForProcess);
}
} else if (stimulus instanceof VariableRateStimulus) {
VariableRateStimulus p = ((VariableRateStimulus) stimulus);
map.put(p, depthCounter);
} else if (stimulus instanceof SingleStimulus) {
SingleStimulus p = ((SingleStimulus) stimulus);
map.put(p, depthCounter);
} else if (stimulus instanceof RelativePeriodicStimulus) {
RelativePeriodicStimulus s = ((RelativePeriodicStimulus) stimulus);
map.put(s, depthCounter);
} else if (stimulus instanceof PeriodicSyntheticStimulus) {
PeriodicSyntheticStimulus p = ((PeriodicSyntheticStimulus) stimulus);
map.put(p, depthCounter);
}
} else {
// not in filter
}
}
return map;
}
private static Amalthea getContainingModel(EObject object) {
if (object == null) return null;
return AmaltheaServices.getContainerOfType(object, Amalthea.class);
}
}