| /******************************************************************************* |
| * Copyright (c) 2015, 2020 Dortmund University of Applied Sciences and Arts 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: |
| * Dortmund University of Applied Sciences and Arts - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.app4mc.multicore.openmapping.model; |
| |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.app4mc.amalthea.model.ActivityGraph; |
| import org.eclipse.app4mc.amalthea.model.ActivityGraphItem; |
| import org.eclipse.app4mc.amalthea.model.CoreClassifier; |
| import org.eclipse.app4mc.amalthea.model.Group; |
| import org.eclipse.app4mc.amalthea.model.ITimeDeviation; |
| import org.eclipse.app4mc.amalthea.model.ModeSwitch; |
| import org.eclipse.app4mc.amalthea.model.PeriodicStimulus; |
| import org.eclipse.app4mc.amalthea.model.ProbabilitySwitch; |
| import org.eclipse.app4mc.amalthea.model.ReferenceObject; |
| 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.Stimulus; |
| import org.eclipse.app4mc.amalthea.model.Tag; |
| import org.eclipse.app4mc.amalthea.model.Task; |
| import org.eclipse.app4mc.amalthea.model.Time; |
| import org.eclipse.app4mc.amalthea.model.Value; |
| import org.eclipse.app4mc.multicore.sharelibs.UniversalHandler; |
| import org.eclipse.core.runtime.IStatus; |
| |
| public class OMTask { |
| private static final String sTokenInclude = "[ConstraintInclude]"; |
| private static final String sTokenExclude = "[ConstraintExclude]"; |
| |
| private final Task taskRef; |
| private OMTask predecessor = null; |
| private long iInstructionCount = -1; |
| private long iRunnableCount = -1; |
| private long iPeriod = -1; |
| /** |
| * Max period among all tasks, used for recursion factor calculation |
| */ |
| private static long iMaxPeriod = -1; |
| |
| private ArrayList<OMAnnotationElement> validTags = new ArrayList<>(); |
| private ArrayList<OMAnnotationElement> invalidTags = new ArrayList<>(); |
| |
| public OMTask(final Task taskRef) { |
| this.taskRef = taskRef; |
| addAnnotationElements(); |
| } |
| |
| public OMTask(final Task taskRef, final OMTask predecessor) { |
| this(taskRef); |
| this.predecessor = predecessor; |
| } |
| |
| private void addAnnotationElements() { |
| final Map<String, Value> itPropertyConstraints; |
| // Check custom Properties |
| if (this.getTaskRef().getCustomProperties().size() > 0) { |
| itPropertyConstraints = this.getTaskRef().getCustomProperties().map(); |
| itPropertyConstraints.forEach((k, v) -> parsePropertyConstraint(k, v)); |
| } |
| } |
| |
| // TODO This should be handled more compact |
| private void parsePropertyConstraint(String k, Value v) { |
| UniversalHandler.getInstance().logCon("T: " + taskRef.getName() + " - Parsing (K: " + k + " V: " + v + ")"); |
| // Check if we have a valid Property Constraint |
| if (v instanceof ReferenceObject) { |
| ReferenceObject ref = (ReferenceObject) v; |
| if (ref.getValue() instanceof Tag) { |
| OMTag ot = new OMTag((Tag) ref.getValue()); |
| if (k.contains(sTokenInclude)) { |
| this.validTags.add(ot); |
| } else if (k.contains(sTokenExclude)) { |
| this.invalidTags.add(ot); |
| } else { |
| // Skipping |
| } |
| } |
| |
| if (ref.getValue() instanceof CoreClassifier) { |
| OMCoreClassifier ot = new OMCoreClassifier((CoreClassifier) ref.getValue()); |
| if (k.contains(sTokenInclude)) { |
| this.validTags.add(ot); |
| } else if (k.contains(sTokenExclude)) { |
| this.invalidTags.add(ot); |
| } else { |
| // Skipping |
| } |
| } |
| } |
| } |
| |
| private void fetchRunnableAndInstructionCount() { |
| // Check if a call graph is present in the task and if it is valid |
| final ActivityGraph callGraph = this.taskRef.getActivityGraph(); |
| if (callGraph == null) { |
| UniversalHandler.getInstance().log("Invalid Software Model, Task '" + this.taskRef.getName() |
| + "' has an invalid or missing containment to its CallGraph.", null); |
| return; |
| } |
| |
| if (callGraph.getItems().isEmpty()) { |
| UniversalHandler.getInstance().log( |
| "Invalid Software Model, The CallGraph of Task '" + this.taskRef.getName() + "' is empty", null); |
| return; |
| } |
| |
| // Runnable counter is increased by the child methods. Since we init it |
| // with -1, we have to increase it one time |
| // manually. |
| ++this.iRunnableCount; |
| |
| this.iInstructionCount = processCallGraph(callGraph); |
| } |
| |
| /** |
| * |
| * @param callGraph |
| * @return |
| */ |
| private long processCallGraph(final ActivityGraph callGraph) { |
| // Count the total number of Instructions of the CallGraph |
| long tmpInstr = 0; |
| |
| // Create iterator with call graph entries (Runnables) and process them |
| final Iterator<ActivityGraphItem> itGraphEntries = callGraph.getItems().iterator(); |
| while (itGraphEntries.hasNext()) { |
| // Check GraphEntry Specialization and process it accordingly |
| final ActivityGraphItem graphEntry = itGraphEntries.next(); |
| // CallSequence |
| if (graphEntry instanceof Group) { |
| tmpInstr += processGroup((Group) graphEntry); |
| } |
| |
| // LabelSwitch |
| else if (graphEntry instanceof ModeSwitch) { |
| UniversalHandler.getInstance().log( |
| "GraphEntry specialisation LabelSwitch is not supported. Skipping...", null, IStatus.WARNING); |
| } |
| |
| // ProbabilitySwitch |
| else if (graphEntry instanceof ProbabilitySwitch) { |
| UniversalHandler.getInstance().log( |
| "GraphEntry specialisation ProbabilitySwitch is not supported. Skipping...", null, |
| IStatus.WARNING); |
| } |
| |
| // Unhandled, default Error |
| else { |
| UniversalHandler.getInstance().log("Unkown GraphEntry specialisation. Skipping...", null); |
| } |
| } |
| return tmpInstr; |
| } |
| |
| /** |
| * Processes the <code>CallSequence</code> and fetches the number of |
| * Instructions for each of its <code>RunnableCalls</code>. Further |
| * handled elements may be added in the future. |
| * |
| * @param group |
| * The CallSequence to process |
| * @return Number of Instructions in the CallSequence |
| */ |
| private long processGroup(final Group group) { |
| // Count the total number of Instructions of the CallSequence |
| long tmpInstr = 0; |
| |
| // Check if CallSequence is empty |
| if (group.getItems().isEmpty()) { |
| UniversalHandler.getInstance().log("Invalid Software Model, Group must not be empty. Skipping...", |
| null); |
| return tmpInstr; |
| } |
| |
| // Process CallSequence items |
| final Iterator<ActivityGraphItem> itCallSeqItems = group.getItems().iterator(); |
| while (itCallSeqItems.hasNext()) { |
| final ActivityGraphItem callSeqEntry = itCallSeqItems.next(); |
| if (callSeqEntry instanceof RunnableCall) { |
| tmpInstr += processRunnableCall((RunnableCall) callSeqEntry); |
| } else { |
| UniversalHandler.getInstance().logWarn("Unkown CallSequenceItem specialisation. Skipping..."); |
| } |
| } |
| return tmpInstr; |
| } |
| |
| /** |
| * Processes the <code>RunnableCall</code> and fetches the number of |
| * Instructions for each of its <code>Runnable</code>s. |
| * |
| * @param RunnableCall |
| * The RunnableCall to process |
| * @return Number of Instructions in the RunnableCall |
| */ |
| private long processRunnableCall(final RunnableCall RunnableCall) { |
| final Runnable runnable = RunnableCall.getRunnable(); |
| // Check if reference to runnable is set |
| if (runnable == null) { |
| UniversalHandler.getInstance() |
| .log("Invalid Software Model, reference to Runnable must not be empty. Skipping...", null); |
| return 0; |
| } |
| |
| ++this.iRunnableCount; |
| final OMRunnable r = new OMRunnable(runnable); |
| // TODO The Runnables should be stored in some way, maybe even the Graph |
| // might be build at this point |
| return r.getInstructionCount(); |
| } |
| |
| @Override |
| public String toString() { |
| return this.taskRef.getUniqueName(); |
| } |
| |
| /** |
| * Get the period period of the task activation. |
| * |
| * @param t |
| * @return Period of the task in pico seconds or 0 if the task has no period |
| */ |
| public long getPeriod() { |
| final List<Stimulus> lSt; |
| // Only check for the first stimulus |
| if ((lSt = getTaskRef().getStimuli()) == null || lSt.isEmpty()) { |
| UniversalHandler.getInstance().log("Stimuli not set.\nSkipping Task " + this.taskRef.getName(), null); |
| return iPeriod; |
| } |
| if (iPeriod < 0) { |
| for (Stimulus s : lSt) { |
| if (s instanceof PeriodicStimulus) { |
| PeriodicStimulus ps = (PeriodicStimulus) s; |
| Time x = ps.getRecurrence(); |
| if (x == null) { |
| iPeriod = 0; |
| return iPeriod; |
| } |
| long val = x.getValue().longValue(); |
| if (val == 0 || x.getUnit() == null) { |
| iPeriod = 0; |
| return iPeriod; |
| } |
| switch (x.getUnit()) { |
| case PS: |
| val *= 1; // |
| break; |
| case NS: |
| val *= 1000; // |
| break; |
| case US: |
| val *= 1000000L; // |
| break; |
| case MS: |
| val *= 1000000000L; // |
| break; |
| case S: |
| val *= 1000000000000L; // |
| break; |
| default: |
| iPeriod = 0; |
| return iPeriod; |
| } |
| iPeriod = val; |
| if (iPeriod > iMaxPeriod) { |
| iMaxPeriod = iPeriod; |
| } |
| |
| return iPeriod; |
| } |
| // Support fot FMTV |
| if (s instanceof RelativePeriodicStimulus) { |
| RelativePeriodicStimulus spst = (RelativePeriodicStimulus) s; |
| ITimeDeviation dev = spst.getNextOccurrence(); |
| Time x = dev.getLowerBound(); |
| if (x == null) { |
| iPeriod = 0; |
| return iPeriod; |
| } |
| long val = x.getValue().longValue(); |
| if (val == 0 || x.getUnit() == null) { |
| iPeriod = 0; |
| return iPeriod; |
| } |
| switch (x.getUnit()) { |
| case PS: |
| val *= 1; // |
| break; |
| case NS: |
| val *= 1000; // |
| break; |
| case US: |
| val *= 1000000L; // |
| break; |
| case MS: |
| val *= 1000000000L; // |
| break; |
| case S: |
| val *= 1000000000000L; // |
| break; |
| default: |
| iPeriod = 0; |
| return iPeriod; |
| } |
| iPeriod = val; |
| if (iPeriod > iMaxPeriod) { |
| iMaxPeriod = iPeriod; |
| } |
| return iPeriod; |
| } |
| |
| } |
| iPeriod = 0;// not periodic |
| } |
| return iPeriod; |
| } |
| |
| /** |
| * Returns the recursion factor of a Task |
| * |
| * The recursion factor is the quotient of the max period among all tasks |
| * and the period of this task, e.g. if the max period is 100ms, and this |
| * tasks period is 50ms, the recursion factor is 0.5. |
| * |
| * @return Double The recursion factor RF with 0.0 < RF <= 1.0 |
| */ |
| public double getRecursionFactor() { |
| if (iPeriod < 0) { |
| getPeriod(); |
| } |
| return iMaxPeriod / (double) iPeriod; |
| } |
| |
| public Task getTaskRef() { |
| return this.taskRef; |
| } |
| |
| public OMTask getPredecessor() { |
| return this.predecessor; |
| } |
| |
| public long getInstructionCount() { |
| if (0 > this.iInstructionCount) { |
| fetchRunnableAndInstructionCount(); |
| } |
| return this.iInstructionCount; |
| } |
| |
| public long getRunnableCount() { |
| if (0 > this.iRunnableCount) { |
| fetchRunnableAndInstructionCount(); |
| } |
| return this.iRunnableCount; |
| } |
| |
| public ArrayList<OMAnnotationElement> getValidAnnotationElements() { |
| return this.validTags; |
| } |
| |
| public ArrayList<OMAnnotationElement> getInvalidAnnotationElements() { |
| return this.invalidTags; |
| } |
| |
| public static void init() { |
| iMaxPeriod = -1; |
| } |
| |
| public static long getMaxPeriod() { |
| return iMaxPeriod; |
| } |
| } |