/******************************************************************************* | |
* Copyright (c) 2020 Dortmund University of Applied Sciences and Arts. | |
* | |
* This program and the accompanying materials | |
* are made available under the terms of the Eclipse Public License 2.0 | |
* which accompanies this distribution, and is available at | |
* https://www.eclipse.org/legal/epl-2.0/ | |
* | |
* SPDX-License-Identifier: EPL-2.0 | |
* | |
* Contributors: The Bao Bui | |
* FH Dortmund - initial API and implementation | |
*******************************************************************************/ | |
package org.eclipse.app4mc.gsoc_rta; | |
import java.math.BigDecimal; | |
import java.math.BigInteger; | |
import java.math.RoundingMode; | |
import java.text.DecimalFormat; | |
import java.util.ArrayList; | |
import java.util.Arrays; | |
import java.util.Collections; | |
import java.util.List; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
import org.eclipse.app4mc.amalthea.model.Amalthea; | |
import org.eclipse.app4mc.amalthea.model.AmaltheaServices; | |
import org.eclipse.app4mc.amalthea.model.ProcessingUnit; | |
import org.eclipse.app4mc.amalthea.model.Runnable; | |
import org.eclipse.app4mc.amalthea.model.Task; | |
import org.eclipse.app4mc.amalthea.model.Time; | |
import org.eclipse.app4mc.amalthea.model.TimeUnit; | |
import org.eclipse.app4mc.amalthea.model.util.FactoryUtil; | |
import org.eclipse.app4mc.amalthea.model.util.SoftwareUtil; | |
import org.eclipse.app4mc.amalthea.model.util.RuntimeUtil.TimeType; | |
import org.eclipse.emf.common.util.EList; | |
public class NPandPRTA { | |
private final int[] ia; | |
private final Amalthea model; | |
private final int[] preemptionTypeArray; | |
private boolean schedubilityCheck = true; | |
//Adding decimal Format for better visual checking when debugging | |
//ie 6000000000 ps -> 6,000,000,000 ps | |
private DecimalFormat df = new DecimalFormat("#,###.##"); | |
final Logger log = LoggerFactory.getLogger(NPandPRTA.class); | |
boolean logDebug = log.isDebugEnabled(); | |
boolean logInfo = log.isInfoEnabled(); | |
boolean logError = log.isErrorEnabled(); | |
/* | |
* PreemptionTypeArray will have the same length with mapping indexArray (ia), just with different value | |
* This will be set up when the class is initialize, the meaning of the number in array are: | |
* 0 = preemptive | |
* 1 = non-preemptive | |
* 2 = cooperative | |
* | |
* ia = { 5, 1, 5, 0, 1, 0, 2, 1, 2, 1, 6, 3, 4, 6 } - mapping array | |
* pta = { 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,} - the preemption type array | |
* this mean the first and second task of the model have non-preemption mode, the rest are preemptive | |
*/ | |
public boolean isSchedubilityCheck() { | |
return schedubilityCheck; | |
} | |
/**Default = true. | |
* | |
* True = return result after comparing task's blocking time and period. | |
* False = return result as long as ultilization value is less than 100 percent. | |
* @param schedubilityCheck | |
*/ | |
public void setSchedubilityCheck(boolean schedubilityCheck) { | |
this.schedubilityCheck = schedubilityCheck; | |
} | |
public NPandPRTA(final Amalthea modelp, final int[] iap) { | |
this.ia = iap; | |
this.model = modelp; | |
this.preemptionTypeArray = new int[iap.length]; | |
setupPTArray(); | |
} | |
/** | |
* Calculate classic RTA of task using this formula Ri = Ci + Sum j among HP(i)[Ri/Tj]*Cj | |
* https://www.it.uu.se/edu/course/homepage/realtid/ht10/schedule/Scheduling-periodic.pdf | |
* @param thisTask | |
* @param thisIA | |
* @return | |
*/ | |
public Time getResponseTimeViaRecurrenceRelation(Task thisTask, int[] ia, final TimeType executionCase) { | |
Time result = FactoryUtil.createTime(BigInteger.ZERO, TimeUnit.PS); | |
/*Initialize variable to compare previous and current iteration in the formula*/ | |
BigDecimal temp = BigDecimal.valueOf(2.0); | |
int[] thisIA = ia.clone(); | |
CPURta rtaia = new CPURta(this.model); | |
List<Task> groupTaskList = groupTask(thisTask, thisIA); | |
List<Task> sortedTaskList = rtaia.taskSorting(groupTaskList); | |
/* Check ultilization value here, if ultiValue > 100% then ultiCheck = false*/ | |
boolean ultiCheck = getUltilizationCheck(sortedTaskList, thisIA, executionCase); | |
if (!ultiCheck) { | |
//result is 0 | |
return result; | |
} | |
int taskIndex = sortedTaskList.indexOf(thisTask); | |
//Loop through all task inside the list (until thisTask) | |
for (int i = 0; i < taskIndex + 1; i++) { | |
//Highest priority -> get executed first all the damn time | |
if (taskIndex == 0) { | |
if (logDebug) { | |
String exeTimeString = df.format(getExecutionTime(thisTask, thisIA, executionCase).getValue()); | |
log.debug("{} ps - Task [{}] recurrence response time", exeTimeString, thisTask.getName()); | |
} | |
return getExecutionTime(thisTask, thisIA, executionCase); | |
} | |
//Calculate rta by looping until the result of the previous and current are the same | |
else if (i == taskIndex) { | |
BigDecimal rZero = BigDecimal.ZERO; | |
/*Set up r_0 for the calculation*/ | |
for (int j = 0; j < i + 1; j++) { | |
Task jTask = sortedTaskList.get(j); | |
Time jTaskExecutionTime = getExecutionTime(jTask, thisIA, executionCase); | |
BigDecimal bigDecVal = new BigDecimal(jTaskExecutionTime.getValue()); | |
rZero = rZero.add(bigDecVal); | |
} | |
BigDecimal currentVal = rZero; | |
//iterate until current value = previous value | |
while (!temp.equals(currentVal)) { | |
temp = currentVal; | |
/** | |
* The iteration loop of | |
* Ri = Ci + Sum of all hp(i)[Ri/Tj]*Cj (round up) | |
* k loop are the part after the Sum symbol, after the loop we add Ci | |
*/ | |
BigDecimal iteration = BigDecimal.ZERO; | |
for (int k = 0; k < i; k++) { | |
Task kTask = sortedTaskList.get(k); | |
Time kTaskExecutionTime = getExecutionTime(kTask, thisIA, executionCase); | |
Time kTaskPeriod = CommonUtils.getStimInTime(kTask); | |
//Need to change all time value to PS for calculation | |
BigInteger kTaskPeriodInPs = AmaltheaServices.convertToPicoSeconds(kTaskPeriod); | |
//execution time and period in bigDecimal | |
BigDecimal kTaskExetimeDec = new BigDecimal(kTaskExecutionTime.getValue()); | |
BigDecimal kTaskPeriodDec = new BigDecimal(kTaskPeriodInPs); | |
// multiplyPart = [Ri/Tj] * Cj | |
// RiTj = roundupPart, Cj = kTaskExetimeDec | |
BigDecimal roundUpPart = currentVal.divide(kTaskPeriodDec, 0, RoundingMode.CEILING); | |
BigDecimal multiplyPart = roundUpPart.multiply(kTaskExetimeDec); | |
iteration = iteration.add(multiplyPart); | |
} | |
//Adding Ci after the iteration | |
Task iTask = sortedTaskList.get(i); | |
BigDecimal iTaskExecutionTimeDec = new BigDecimal(getExecutionTime(iTask, thisIA, executionCase).getValue()); | |
currentVal = iteration.add(iTaskExecutionTimeDec); | |
} | |
BigDecimal thisTaskPeriodInBigDec = new BigDecimal(AmaltheaServices.convertToPicoSeconds(CommonUtils.getStimInTime(thisTask))); | |
if (currentVal.compareTo(thisTaskPeriodInBigDec) > 0 && this.schedubilityCheck) { | |
if (logError) { | |
String currentValString = df.format(currentVal); | |
String thisTaskPeriodInBigDecString = df.format(thisTaskPeriodInBigDec); | |
log.error("!!! UNSCHEDULABLE !!!"); | |
log.error("Response time of task [{}] is bigger than its period, return 0 for RT ", thisTask.getName()); | |
log.error("{} > {}", currentValString, thisTaskPeriodInBigDecString); | |
} | |
//result is 0 | |
return result; | |
} | |
if (logInfo) { | |
String currentValString = df.format(currentVal); | |
log.info("{} ps - Task [{}] recurrence response time", currentValString, thisTask.getName()); | |
} | |
//result is currentVal calculated above | |
result.setValue(currentVal.toBigInteger()); | |
} | |
} | |
return result; | |
} | |
/** | |
* Calculate RT of thisTask using busy window technique (level-i busy period) | |
* No blocking time is present here. This function is used for just simple preemptive environment case | |
* @param thisTask | |
* @param ia | |
* @param executionCase | |
* @return Time responseTime | |
*/ | |
public Time getResponseTimeViaLvI(Task thisTask, int[] ia, TimeType executionCase) { | |
Time responseTime = FactoryUtil.createTime(BigInteger.ZERO, TimeUnit.PS); | |
int[] thisIA = ia.clone(); | |
CPURta rtaia = new CPURta(this.model); | |
List<Task> groupTaskList = groupTask(thisTask, thisIA); | |
List<Task> sortedTaskList = rtaia.taskSorting(groupTaskList); | |
/* Check ultilization value here, if ultiValue > 100% then ultiCheck = false*/ | |
boolean ultiCheck = getUltilizationCheck(sortedTaskList, thisIA, executionCase); | |
if (!ultiCheck) { | |
//result is 0 | |
return responseTime; | |
} | |
int taskIndex = sortedTaskList.indexOf(thisTask); | |
Time iPeriodTime = CommonUtils.getStimInTime(thisTask); | |
BigInteger iPeriodInPs = AmaltheaServices.convertToPicoSeconds(iPeriodTime); | |
Time iExecutionTime = getExecutionTime(thisTask, thisIA, executionCase); | |
/* ----- Get LEVEL - I for each task ------ */ | |
BigDecimal lv0 = BigDecimal.ZERO; | |
BigDecimal currentLv = BigDecimal.ONE; | |
BigDecimal iPeriod = new BigDecimal(iPeriodInPs); | |
BigDecimal iExe = new BigDecimal(iExecutionTime.getValue()); | |
BigDecimal iResponseTime = BigDecimal.ZERO; | |
while (!currentLv.equals(lv0)) { | |
lv0 = currentLv; | |
//entry = each small sum of [Li/Tj]Cj, p_j >= p_i ( i = taskIndex) | |
BigDecimal iterationValue = BigDecimal.ZERO; | |
for (int entry = 0; entry < taskIndex + 1; entry++) { | |
Task entryTask = sortedTaskList.get(entry); | |
BigInteger entryPeriodInPs = AmaltheaServices.convertToPicoSeconds(CommonUtils.getStimInTime(entryTask)); | |
BigDecimal entryPeriod = new BigDecimal(entryPeriodInPs); | |
BigDecimal entryExe = new BigDecimal(getExecutionTime(entryTask, thisIA, executionCase).getValue()); | |
BigDecimal roundUp = currentLv.divide(entryPeriod, 0, RoundingMode.CEILING); | |
BigDecimal entryValue = roundUp.multiply(entryExe); | |
if (logDebug) { | |
String roundUpString = df.format(roundUp); | |
String entryValueString = df.format(entryValue); | |
log.debug("{} round up x {}entryValue", roundUpString, entryValueString); | |
} | |
iterationValue = iterationValue.add(entryValue); | |
} | |
currentLv = iterationValue; | |
} | |
if (logDebug) { | |
String lv0String = df.format(lv0); | |
log.debug("{} ps - level-i ", lv0String); | |
} | |
/* ----- Get K (total task iteration) ------ */ | |
int bigK = lv0.divide(iPeriod, 0, RoundingMode.CEILING).intValue(); | |
/* ----- Get finishing time for each task ------ */ | |
for (int smallK = 1; smallK <= bigK; smallK++) { | |
log.debug(" --- k instance : {}/{} --------------------------------------------", smallK, bigK); | |
BigDecimal kLv = BigDecimal.ZERO; | |
BigDecimal currentKLv = BigDecimal.ONE; | |
BigDecimal kCi = iExe.multiply(BigDecimal.valueOf(smallK)); | |
int smallK1 = smallK - 1; | |
int counter = 0; | |
while (!kLv.equals(currentKLv)) { | |
currentKLv = kLv; | |
//entry = each small sum of [Li/Tj]Cj, p_j > p_i, not >= | |
BigDecimal iterationValue = BigDecimal.ZERO; | |
for (int entry = 0; entry < taskIndex; entry++) { | |
Task entryTask = sortedTaskList.get(entry); | |
BigInteger entryPeriodInPs = AmaltheaServices.convertToPicoSeconds(CommonUtils.getStimInTime(entryTask)); | |
BigDecimal entryPeriod = new BigDecimal(entryPeriodInPs); | |
BigDecimal entryExe = new BigDecimal(getExecutionTime(entryTask, thisIA, executionCase).getValue()); | |
BigDecimal roundUp = currentKLv.divide(entryPeriod, 0, RoundingMode.CEILING); | |
BigDecimal entryValue = roundUp.multiply(entryExe); | |
iterationValue = iterationValue.add(entryValue); | |
if (counter == 0) { | |
iterationValue = BigDecimal.ZERO; | |
counter++; | |
break; | |
} | |
} | |
kLv = iterationValue; | |
kLv = kLv.add(kCi); //add k*Ci at the end of iteration | |
if (logDebug) { | |
log.debug("finishTime = iterationValue + k*Ci: "); | |
String s1 = df.format(iterationValue); | |
String s2 = df.format(kCi); | |
String s3 = df.format(kLv); | |
log.debug("{} + {} + {}", s1, s2, s3); | |
} | |
} | |
/* ----- Get response time for each task (k) ------ */ | |
BigDecimal taskStartTime = iPeriod.multiply(BigDecimal.valueOf(smallK1)); | |
if (logDebug) { | |
String s1 = df.format(taskStartTime); | |
String s2 = df.format(kLv); | |
log.debug("start time (k*period): {} - finishing time {}", s1, s2); | |
} | |
BigDecimal taskResponseTime = kLv.subtract(taskStartTime); | |
if (logDebug) { | |
String s1 = df.format(taskResponseTime); | |
log.debug("{} - this k instance response time", s1); | |
} | |
if (taskResponseTime.compareTo(iResponseTime) >= 0) { | |
iResponseTime = taskResponseTime; | |
} | |
if (logDebug) { | |
String s1 = df.format(iResponseTime); | |
log.debug("{} - biggest response time so far ", s1); | |
} | |
} | |
String taskName = thisTask.getName(); | |
if (logInfo) { | |
String s1 = df.format(iResponseTime); | |
log.info("{} ps - Task [{}] level-i response time", s1, taskName); | |
} | |
//Check schedulability | |
if (iResponseTime.compareTo(iPeriod) > 0 && this.schedubilityCheck) { | |
log.error("!!! UNSCHEDULABLE !!!"); | |
log.error("Response time of task [{}] is bigger than its period, return 0 for RT ", taskName); | |
if (logError) { | |
String s1 = df.format(iResponseTime); | |
String s2 = df.format(iPeriod); | |
log.error("{} > {} ", s1, s2); | |
} | |
//result is 0 | |
return responseTime; | |
} | |
//Set response time value after all calculation above | |
responseTime.setValue(iResponseTime.toBigInteger()); | |
return responseTime; | |
} | |
/** | |
* Calculate the execution time of longest/biggest runnable within task. | |
* @param thisTask | |
* @param ia | |
* @param executionCase | |
* @return | |
*/ | |
private Time getLongestRunnableExecutionTime(Task thisTask, int[] ia, TimeType executionCase) { | |
//set up decimal format for debugging | |
DecimalFormat df = new DecimalFormat("#,###.##"); | |
final int currentIndex = this.model.getSwModel().getTasks().indexOf(thisTask); //get index of the task in the array | |
final int PUI = ia[currentIndex]; // get the index of processing unit | |
final List<ProcessingUnit> pul = CommonUtils.getPUs(this.model); | |
final ProcessingUnit pu = pul.get(PUI); | |
//comparing value to get the longer one | |
Time longest = FactoryUtil.createTime(BigInteger.ZERO, TimeUnit.PS); | |
final List<Runnable> runnableList = SoftwareUtil.getRunnableList(thisTask, null); | |
RTARuntimeUtil rtautil = new RTARuntimeUtil(); | |
//In case a task doesn't invoke any runnable, better be careful than be sorry | |
if (!runnableList.isEmpty()) { | |
for (Runnable r : runnableList) { | |
Time runnableTime = rtautil.getExecutionTimeForRTARunnable(r, pu, executionCase); | |
if (longest.compareTo(runnableTime) <= 0) { | |
longest = runnableTime; | |
} | |
} | |
} | |
if (runnableList.isEmpty()) { | |
log.error("!!!The task [{}] is doing nothing, no runnable called",thisTask.getName()); | |
} | |
if (logDebug) | |
{ | |
String s1 = df.format(longest.getValue()); | |
log.debug("{} ps - Longest runnable of task [{}]", s1, thisTask.getName()); | |
} | |
return longest; | |
} | |
/** | |
* Use when task is non-preemptive | |
* Calculation thisTask's response time via level-i busy window technique. | |
* reason is for non-preemp task, finishing time = start time + execution time. | |
* Response time = finishing time - period, (using k-instance) | |
* @param thisTask | |
* @param ia | |
* @param executionCase | |
* @param pTypeArray - leave null if don't use | |
* @boolean usePtypeArray - true if use pType array, otherwise false | |
* @return | |
*/ | |
public Time getNonPreemptaskRTA(Task thisTask, int[] ia, TimeType executionCase, int[] pTypeArray, boolean usePtypeArray) { | |
//Adding decimal Format for better visual checking when debugging | |
//ie 6000000000 ps -> 6,000,000,000 ps | |
Time responseTime = FactoryUtil.createTime(BigInteger.ZERO, TimeUnit.PS); | |
//time that lp tasks may block thisTask in worst case response time | |
BigDecimal blockingTime = new BigDecimal(getLowerPriorityBlockingTime(thisTask, ia, executionCase, pTypeArray, usePtypeArray).getValue()); | |
CPURta rtaia = new CPURta(this.model); | |
int[] thisIA = ia.clone(); | |
List<Task> sortedTaskList = rtaia.taskSorting(groupTask(thisTask, thisIA)); | |
/* Check ultilization value here, if ultiValue > 100% then ultiCheck = false*/ | |
boolean ultiCheck = getUltilizationCheck(sortedTaskList, thisIA, executionCase); | |
if (!ultiCheck) { | |
//response time = 0 here, cuz the core would be damned if we use this set | |
return responseTime; | |
} | |
int taskIndex = sortedTaskList.indexOf(thisTask); | |
Time iPeriodTime = CommonUtils.getStimInTime(thisTask); | |
BigInteger iPeriodInPs = AmaltheaServices.convertToPicoSeconds(iPeriodTime); | |
Time iExecutionTime = getExecutionTime(thisTask, thisIA, executionCase); | |
/* ----- Get LEVEL - I for each task ------ */ | |
BigDecimal lv0 = BigDecimal.ZERO; | |
BigDecimal currentLv = BigDecimal.ONE; | |
BigDecimal iPeriod = new BigDecimal(iPeriodInPs); | |
BigDecimal iExe = new BigDecimal(iExecutionTime.getValue()); | |
BigDecimal iResponseTime = BigDecimal.ZERO; | |
String taskName = thisTask.getName(); | |
/* If blocking time is equal/bigger than task period, then unschedulable for sure, | |
* just return 0 and warning, no need for further calculation*/ | |
if (blockingTime.compareTo(iPeriod) >= 0 && this.schedubilityCheck) { | |
log.error("!!! UNSCHEDULABLE !!!"); | |
log.error("Blocking time of task [{}] is bigger than its period, return 0 for RT ", taskName); | |
if (logError) { | |
String s1 = df.format(blockingTime); | |
String s2 = df.format(iPeriod); | |
log.error("{} > {} ", s1, s2); | |
} | |
//result is 0 | |
return responseTime; | |
} | |
//sum of execution time of all tasks with higher prio not including this task (in TU Dortmund say included, they are imbecile ) | |
BigDecimal hpCiSum = BigDecimal.ZERO; | |
int iterationCounterLvI = 0; | |
while (!currentLv.equals(lv0)) { | |
lv0 = currentLv; | |
//entry = each small sum of [Li/Tj]Cj, p_j >= p_i ( i = taskIndex) | |
BigDecimal iterationValue = BigDecimal.ZERO; | |
for (int entry = 0; entry < taskIndex + 1; entry++) { | |
Task entryTask = sortedTaskList.get(entry); | |
BigInteger entryPeriodInPs = AmaltheaServices.convertToPicoSeconds(CommonUtils.getStimInTime(entryTask)); | |
BigDecimal entryPeriod = new BigDecimal(entryPeriodInPs); | |
BigDecimal entryExe = new BigDecimal(getExecutionTime(entryTask, thisIA, executionCase).getValue()); | |
BigDecimal roundUp = currentLv.divide(entryPeriod, 0, RoundingMode.CEILING); | |
BigDecimal entryValue = roundUp.multiply(entryExe); | |
iterationValue = iterationValue.add(entryValue); | |
} | |
//First entry of level-i = Bi + Ci, the sum above doesn't exist (or maybe 1, not gonna risk it hence the code below) | |
//check non-premptive scheduler from TUDortmund pdf, google that shit | |
if (iterationCounterLvI == 0) { | |
iterationValue = iExe; | |
} | |
iterationCounterLvI++; | |
/* -- Add blocking for nonpreemptive here-- current lv plus blocking*/ | |
iterationValue = iterationValue.add(blockingTime); | |
currentLv = iterationValue; | |
} | |
if (logDebug) { | |
String lv0String = df.format(lv0); | |
log.debug("{} ps - level-i ", lv0String); | |
} | |
/* ----- Get K (total task iteration) ------ */ | |
int bigK = lv0.divide(iPeriod, 0, RoundingMode.CEILING).intValue(); | |
/* ----- Get start time for each task ------ */ | |
/** | |
* Things get more complicated from here, we get preemption from higher prio, blocking from lower prio | |
* so we have to calculate the START TIMEEEE with blocking, my man | |
*/ | |
for (int smallK = 1; smallK <= bigK; smallK++) { | |
log.debug(" --- k instance : {}/{} --------------------------------------------", smallK, bigK); | |
BigDecimal kLv = BigDecimal.ZERO; | |
BigDecimal currentKLv = BigDecimal.ONE; | |
int smallK1 = smallK - 1; | |
BigDecimal kCi1 = iExe.multiply(BigDecimal.valueOf(smallK1)); | |
//Equation from Tu Dortmund for this is wrong, it's < taskIndex, not < i. | |
for (int entry = 0; entry < taskIndex; entry++) { | |
Task entryTask = sortedTaskList.get(entry); | |
BigDecimal entryExe = new BigDecimal(getExecutionTime(entryTask, thisIA, executionCase).getValue()); | |
hpCiSum = hpCiSum.add(entryExe); | |
} | |
int iterationCounterK = 0; | |
while (!kLv.equals(currentKLv)) { | |
currentKLv = kLv; | |
//entry = each small sum of [Li/Tj]Cj (rown down), j > i, not >= | |
BigDecimal iterationValue = BigDecimal.ZERO; | |
for (int entry = 0; entry < taskIndex; entry++) { | |
Task entryTask = sortedTaskList.get(entry); | |
BigInteger entryPeriodInPs = AmaltheaServices.convertToPicoSeconds(CommonUtils.getStimInTime(entryTask)); | |
BigDecimal entryPeriod = new BigDecimal(entryPeriodInPs); | |
BigDecimal entryExe = new BigDecimal(getExecutionTime(entryTask, thisIA, executionCase).getValue()); | |
BigDecimal roundDown = currentKLv.divide(entryPeriod, 0, RoundingMode.FLOOR); | |
BigDecimal roundDownPlusOne = roundDown.add(BigDecimal.ONE); | |
BigDecimal entryValue = roundDownPlusOne.multiply(entryExe); | |
iterationValue = iterationValue.add(entryValue); | |
} | |
kLv = iterationValue; | |
kLv = kLv.add(kCi1); //add k*Ci at the end of iteration | |
//first iteration = Bi + hpCiSum | |
if (iterationCounterK == 0) { | |
log.debug("hpCiSum - {} blocking {}", hpCiSum, blockingTime); | |
kLv = hpCiSum; | |
//reseti this for next k-task | |
hpCiSum = BigDecimal.ZERO; | |
} | |
log.debug("iterationValue + (k-1)*Ci + blockingtime (ignore if first iteration)"); | |
log.debug("{} + {} + {}", iterationValue, kCi1, blockingTime); | |
kLv = kLv.add(blockingTime); // add blocking time here | |
log.debug("current iteration : {}", kLv); | |
iterationCounterK++; | |
} | |
/* ----- Get response time for each task (k) ------ */ | |
BigDecimal taskStartTime = kLv; | |
BigDecimal taskFinishingTime = taskStartTime.add(iExe); | |
BigDecimal taskStartPeriod = iPeriod.multiply(BigDecimal.valueOf(smallK1)); | |
if (logDebug) { | |
String s1 = df.format(taskStartTime); | |
String s2 = df.format(iExe); | |
String s3 = df.format(taskFinishingTime); | |
String s4 = df.format(taskStartPeriod); | |
log.debug("start time : {} + execution time: {} + finishing time: {}; period: {}", s1, s2, s3, s4); | |
} | |
BigDecimal taskResponseTime = taskFinishingTime.subtract(taskStartPeriod); | |
if (logDebug) { | |
String s1 = df.format(taskResponseTime); | |
log.debug("{} - this k instance response time ", s1); | |
} | |
if (taskResponseTime.compareTo(iResponseTime) >= 0) { | |
iResponseTime = taskResponseTime; | |
} | |
if (logDebug) { | |
String s1 = df.format(iResponseTime); | |
log.debug("{} - biggest response time so far ", s1); | |
} | |
} | |
if (logInfo) { | |
String s1 = df.format(iResponseTime); | |
log.info("{} ps - Task [{}] response time (non-preemp level-i)", s1, taskName); | |
} | |
//Check schedulability | |
if (iResponseTime.compareTo(iPeriod) > 0 && this.schedubilityCheck) { | |
log.error("!!! UNSCHEDULABLE !!!"); | |
log.error("Response time of task [{}] is bigger than its period, return 0 for RT ", taskName); | |
if (logError) { | |
String s1 = df.format(iResponseTime); | |
String s2 = df.format(iPeriod); | |
log.error("{} > {} ", s1, s2); | |
} | |
//result is 0 | |
return responseTime; | |
} | |
//Set response time value after all calculation above | |
responseTime.setValue(iResponseTime.toBigInteger()); | |
return responseTime; | |
} | |
/** | |
* Calculate time that [thisTask] would be blocked by lower priority task (when lp task is non-preemptive type or cooperative type) | |
* @param thisTask | |
* @param ia | |
* @param executionCase | |
* @param pTypeArray - leave null if don't use | |
* @boolean usePtypeArray - true if use pType array, otherwise false | |
* @return Time blockingTime | |
*/ | |
public Time getLowerPriorityBlockingTime(Task thisTask, int[] ia, TimeType executionCase, int[] pTypeArray, boolean usePtypeArray) { | |
Time blockingTime = FactoryUtil.createTime(BigInteger.ZERO, TimeUnit.PS); | |
int[] preemptiveArray; | |
int[] thisIA = ia.clone(); | |
if (usePtypeArray) { | |
preemptiveArray = pTypeArray.clone(); | |
} | |
else { | |
preemptiveArray = this.preemptionTypeArray.clone(); | |
} | |
/*Get list of tasks that are on the same processor, then sort it from higher prio -> lower prio*/ | |
CPURta rtaia = new CPURta(this.model); | |
EList<Task> listTask = this.model.getSwModel().getTasks(); //just get all the task on the list, for fun, or check it yourself below | |
List<Task> groupTaskList = groupTask(thisTask, thisIA); | |
List<Task> sortedTaskList = rtaia.taskSorting(groupTaskList); | |
String taskName = thisTask.getName(); | |
if (logDebug) { | |
String s1 = CommonUtils.getStimInTime(thisTask).toString(); | |
log.debug("-- get blocking time of task [{}] - period: {} ", taskName, s1); | |
} | |
/*Reverse the order from highest -> lowest to the opposite lowest -> highest,whatever in front of our index is lower priority */ | |
Collections.reverse(sortedTaskList); | |
/*------------------- Get blocking time from lp task ------------------*/ | |
/* Loop through all the task in the sorted Task list and until we see thisTask, calculate blocking time while looping, | |
* the longest among all blocking time will be WCBlockingTime for [thisTask] | |
* I have reversed the order of the task list above so we will capture all task with lower priority via top down iteration | |
* lowest -> highest prio | |
* and when we get to [thisTask], that means we have scanned all task with lower priority -> break the loop | |
*/ | |
for (Task task : sortedTaskList) { | |
//Get out of the loop when we met thisTask in the sorted task list | |
//Without this, lowerPriorityTaskList will add the task with higher priority | |
if (task.equals(thisTask)) { | |
break; | |
} | |
Time tempBlockingTime = FactoryUtil.createTime(BigInteger.ZERO, TimeUnit.PS); | |
int taskIndex = listTask.indexOf(task); | |
int preempTypeValue = preemptiveArray[taskIndex];//listTask used to get preemptive type my dude, | |
String preemp = task.getPreemption().getLiteral(); | |
Time taskPeriod = CommonUtils.getStimInTime(task); | |
log.debug(" checking task" + " [{}] - period {}- preemptiveType : {}", task.getName(), taskPeriod, preemp); | |
//If task has lower priority and is non-preemptive (preempTypeValue = 1 ), calculate that task's execution time | |
if (!task.equals(thisTask) && preempTypeValue == 1) { | |
tempBlockingTime = getExecutionTime(task, thisIA, executionCase); | |
if (logDebug) { | |
String s1 = df.format(tempBlockingTime.getValue()); | |
String s2 = task.getName(); | |
log.debug("{} ps - non-preemptive blocking time from task [{}]", s1, s2); | |
} | |
if (tempBlockingTime.compareTo(blockingTime) >= 0) | |
blockingTime = tempBlockingTime; | |
} | |
//If task has lower priority and is cooperative (preempTypeValue = 2 ), calculate the longest runnable | |
else if (!task.equals(thisTask) && preempTypeValue == 2) { | |
tempBlockingTime = getLongestRunnableExecutionTime(task, thisIA, executionCase); | |
if (logDebug) { | |
String s1 = df.format(tempBlockingTime.getValue()); | |
String s2 = task.getName(); | |
log.debug("{} ps - cooperative blocking time from task [{}]", s1, s2); | |
} | |
if (tempBlockingTime.compareTo(blockingTime) >= 0) | |
blockingTime = tempBlockingTime; | |
} | |
/* If all lower priority task are neither non-preemptive nor cooperative, blocking time = 0 (because it can only be preemptive) | |
* These line can be omit but meh, for a clearer picture (or messy I don't know)*/ | |
if (tempBlockingTime.compareTo(blockingTime) >= 0) | |
blockingTime = tempBlockingTime; | |
} | |
if (logDebug) { | |
String s1 = df.format(blockingTime.getValue()); | |
String s2 = thisTask.getName(); | |
log.debug("{} ps - worst case blocking time of task [{}] from lower priority tasks", s1, s2); | |
} | |
return blockingTime; | |
} | |
/** | |
* Calculation uses classic RTA + max(max_{j of type NP and in lp(i)} C_j; max_{k of type C and in lp(i)} c_k. | |
* basically just add a blocking time | |
* (choose the longest time, task A will be block by another task (B/C/D) depends on each's execution time | |
* or each's runnable's execution time) among tasks within the same processor depend on each's preemption mode | |
* @param thisTask | |
* @param ia | |
* @param executionCase | |
* @return | |
*/ | |
public Time getMixedPreempRTA(Task thisTask, int[] ia, TimeType executionCase) { | |
//Adding decimal Format for better visual checking when debugging | |
//ie 6000000000 ps -> 6,000,000,000 ps | |
DecimalFormat df = new DecimalFormat("#,###.##"); | |
EList<Task> listTask = this.model.getSwModel().getTasks(); | |
Time result = FactoryUtil.createTime(BigInteger.ZERO, TimeUnit.PS); | |
Time nonPreempBlockingTime = FactoryUtil.createTime(BigInteger.ZERO, TimeUnit.PS); | |
Time coopBlockingTime = FactoryUtil.createTime(BigInteger.ZERO, TimeUnit.PS); | |
Time taskClassicRTA = getResponseTimeViaRecurrenceRelation(thisTask, ia, executionCase); | |
CPURta rtaia = new CPURta(this.model); | |
List<Task> sortedTaskList = rtaia.taskSorting(groupTask(thisTask, ia)); | |
//Reverse the order from highest -> lowest to the opposite lowest -> highest | |
Collections.reverse(sortedTaskList); | |
List<Task> npLowerPriorityTaskList = new ArrayList<>(); | |
List<Task> coopLowerPriorityTaskList = new ArrayList<>(); | |
/*Loop through all the task in the sorted Task list and add them to lowerPriorityTaskList until we see thisTask | |
* I have reversed the order of the task list above so we will capture all task with lower priority via top down iteration | |
* lowest -> highest prio | |
*/ | |
for (Task task : sortedTaskList) { | |
int taskIndex = listTask.indexOf(task); | |
int preempTypeValue = this.preemptionTypeArray[taskIndex]; | |
//add task to our list if it has lower priority and is non-preemptive (preempTypeValue = 1 ) | |
if (!task.equals(thisTask) && preempTypeValue == 1) { | |
npLowerPriorityTaskList.add(task); | |
} | |
//add task to the list if has lower priority and in cooperative (preempTypeValue = 2 ) | |
else if (!task.equals(thisTask) && preempTypeValue == 2) { | |
coopLowerPriorityTaskList.add(task); | |
} | |
//Get out of the loop when we met thisTask in the sorted task list | |
//Without this, lowerPriorityTaskList may add the task with higher priority | |
else if (task.equals(thisTask)) { | |
break; | |
} | |
} | |
/** | |
* if there is no non-preemptive task with lower priority allocate to the processor | |
*/ | |
if (npLowerPriorityTaskList.isEmpty()) { | |
//blockingTime = 0 initially | |
result = result.add(nonPreempBlockingTime); | |
log.debug(df.format(nonPreempBlockingTime.getValue()) + " ps - Non-preemptive blocking time, no blocking"); | |
} | |
/** | |
* if there is no cooperative task with lower priority in the same processor | |
*/ | |
if (coopLowerPriorityTaskList.isEmpty()) { | |
//coop blocking time = 0 | |
result = result.add(coopBlockingTime); | |
log.debug(df.format(coopBlockingTime.getValue()) + " ps - Cooperative blocking time, no blocking"); | |
} | |
/** | |
* adding blocking time of lower priority task with non-preemptive mode and cooperative | |
* Take the longer blocking time among the 2 | |
* Only block if there either/both task in our task lists (coop + np) | |
*/ | |
if (!npLowerPriorityTaskList.isEmpty() || !coopLowerPriorityTaskList.isEmpty()) { | |
for (Task task : npLowerPriorityTaskList) { | |
int taskIndex = listTask.indexOf(task); | |
Time currentBlockingTime = FactoryUtil.createTime(BigInteger.ZERO, TimeUnit.PS); | |
/** | |
* 1 task can only have either np or coop preemption mode anyway | |
*/ | |
if (this.preemptionTypeArray[taskIndex] == 1) { | |
currentBlockingTime = getExecutionTime(task, ia, executionCase); | |
} | |
else if (this.preemptionTypeArray[taskIndex] == 2) { | |
currentBlockingTime = getLongestRunnableExecutionTime(thisTask, ia, executionCase); | |
} | |
/** | |
* after the above we will have the time that [thisTask] is blocked by [task] potentially | |
* We only need to loop through all task in [listTask] to find which [task] block [thisTask] the longest | |
* | |
*/ | |
//Compare to find the longer one when iterate | |
if (nonPreempBlockingTime.compareTo(currentBlockingTime) <= 0) { | |
nonPreempBlockingTime = currentBlockingTime; | |
} | |
log.debug(df.format(currentBlockingTime.getValue()) + " ps - Lower priority task [" + task.getName() + "] blockingTime"); | |
} | |
//add blocking time when we have find the maximum execution time among | |
result = result.add(nonPreempBlockingTime); | |
log.debug(df.format(nonPreempBlockingTime.getValue()) + " ps - Final Non-preemptive/coop blocking time: "); | |
} | |
//final result should be RTA = blockingTime + classicRTA(task) | |
result = result.add(taskClassicRTA); | |
BigDecimal thisTaskPeriodInBigDec = new BigDecimal(AmaltheaServices.convertToPicoSeconds(CommonUtils.getStimInTime(thisTask))); | |
if (result.getValue().compareTo(thisTaskPeriodInBigDec.toBigInteger()) > 0) { | |
log.error("!!! UNSCHEDULABLE !!!"); | |
log.error("Response time of task [" + thisTask.getName() + "] is bigger than its period, return 0 for RT "); | |
log.error(df.format(result.getValue()) + " > " + df.format(thisTaskPeriodInBigDec)); | |
//result is 0 | |
result.setValue(BigInteger.ZERO); | |
return result; | |
} | |
log.debug(df.format(result.getValue()) + "ps - Task [" + thisTask.getName() + "] NP environment response time :"); | |
return result; | |
} | |
/** | |
* this function will get PURE execution time via calculate execution time of task's runnables | |
* @param thisTask | |
* @param ia | |
* @return | |
*/ | |
public Time getPureExecutionTime(Task thisTask, int[] ia, final TimeType executionCase) { | |
Time result = FactoryUtil.createTime(BigInteger.ZERO, TimeUnit.PS); | |
final int currentIndex = this.model.getSwModel().getTasks().indexOf(thisTask); //get index of the task in the array | |
final int PUI = ia[currentIndex]; // get the index of processing unit | |
final List<ProcessingUnit> pul = CommonUtils.getPUs(this.model); | |
final ProcessingUnit pu = pul.get(PUI); | |
RTARuntimeUtil rtautil = new RTARuntimeUtil(); | |
/* The list of runnables of the given task */ | |
final List<Runnable> runnableList = SoftwareUtil.getRunnableList(thisTask, null); | |
/*Add all execution time of runnable inside thisTask to get its PURE execution time*/ | |
for (Runnable r : runnableList) { | |
Time runnableExecutionTime = rtautil.getExecutionTimeForRTARunnable(r, pu, executionCase); | |
result = result.add(runnableExecutionTime); | |
} | |
return result; | |
} | |
/**May sound stupid in the beginning but this will make things easier to control when | |
* Adding blocking/contention at the end of the function. | |
* @param thisTask | |
* @param ia | |
* @param executionCase | |
* @return | |
*/ | |
private Time getExecutionTime(Task thisTask, int[] ia, final TimeType executionCase) { | |
Time result = FactoryUtil.createTime(BigInteger.ZERO, TimeUnit.PS); | |
result = result.add(getPureExecutionTime(thisTask, ia, executionCase)); | |
/** | |
* Add other blocking/waiting method here if needed. | |
*/ | |
return result; | |
} | |
/** | |
* Get core ultilization percentage by Sum all [Ci/Ti] (i = tasks in the same core) | |
* return false If [Ci/Ti] > 1 or ulti percentage > 100% | |
* groupTask = List<Task> that in the same core | |
* @param groupTask | |
* @param ia | |
* @param executionCase | |
* @return | |
*/ | |
public boolean getUltilizationCheck(List<Task> groupTask, int[] ia, TimeType executionCase) { | |
boolean checkValue = true; | |
BigDecimal ultiValue = BigDecimal.ZERO; | |
int[] thisIA = ia.clone(); | |
for (Task task : groupTask) { | |
Time taskPeriod = CommonUtils.getStimInTime(task); | |
Time taskExe = getExecutionTime(task, thisIA, executionCase); | |
BigDecimal numerator = new BigDecimal(taskExe.getValue()); | |
BigDecimal denominator = new BigDecimal(AmaltheaServices.convertToPicoSeconds(taskPeriod)); | |
//scale = 9 here means 9 digit after the damn commaaaaa, why scale 9? because feng shui my dude. | |
BigDecimal division = numerator.divide(denominator, 9, RoundingMode.HALF_UP); | |
ultiValue = ultiValue.add(division); | |
} | |
log.debug(ultiValue.multiply(BigDecimal.valueOf(100)) + " % - core ultilization Value "); | |
//if ultilization percentage > 100% then core exceed it maximum workable ability, should never happen | |
if (ultiValue.compareTo(BigDecimal.ONE) > 0) { | |
log.error("!!! UNSCHEDULABLE !!!"); | |
log.error("Ultilization value :" + ultiValue.multiply(BigDecimal.valueOf(100)) + " exceed 100% "); | |
checkValue = false; | |
} | |
return checkValue; | |
} | |
/** | |
* Initialize preemptive array. | |
*/ | |
private void setupPTArray() { | |
log.debug("Initialize class NPandPRTA"); | |
EList<Task> listTask = this.model.getSwModel().getTasks(); | |
for (Task task : listTask) { | |
int taskIndex = listTask.indexOf(task); | |
String preemp = task.getPreemption().getLiteral(); | |
if (preemp.matches("preemptive")) { | |
this.preemptionTypeArray[taskIndex] = 0; | |
} | |
else if (preemp.matches("non_preemptive")) { | |
this.preemptionTypeArray[taskIndex] = 1; | |
} | |
else if (preemp.matches("cooperative")) { | |
this.preemptionTypeArray[taskIndex] = 2; | |
} | |
} | |
if (logDebug) | |
{ | |
String s1 = Arrays.toString(this.ia); | |
String s2 = Arrays.toString(this.preemptionTypeArray) ; | |
log.debug("{} - ia", s1); | |
log.debug("{} - pta",s2); | |
} | |
} | |
private List<Task> groupTask(final Task thisTask, int[] ia) { | |
final List<Task> stepOneTaskList = new ArrayList<>(); | |
final EList<Task> taskList = this.model.getSwModel().getTasks(); | |
final int currentIndex = this.model.getSwModel().getTasks().indexOf(thisTask); | |
CPURta rtaia = new CPURta(this.model); | |
final int PUI = ia[currentIndex]; | |
for (int i = 0; i < ia.length; i++) { | |
if (PUI == ia[i]) { | |
stepOneTaskList.add(taskList.get(i)); | |
} | |
} | |
return rtaia.taskSorting(stepOneTaskList); | |
} | |
} |