| /** |
| ******************************************************************************** |
| * 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 -> 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 -> 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 -> 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); |
| } |
| |
| } |