blob: f334f809dfdfabadcea93df94f1a86c7c8356328 [file] [log] [blame]
/**
********************************************************************************
* Copyright (c) 2022 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.amalthea.model.util.stimuli;
import org.eclipse.app4mc.amalthea.model.ModeLabel;
import org.eclipse.app4mc.amalthea.model.PeriodicBurstStimulus;
import org.eclipse.app4mc.amalthea.model.Process;
import org.eclipse.app4mc.amalthea.model.Time;
import org.eclipse.app4mc.amalthea.model.util.FactoryUtil;
import org.eclipse.app4mc.amalthea.model.util.RuntimeUtil;
import org.eclipse.app4mc.amalthea.model.util.RuntimeUtil.TimeType;
import org.eclipse.emf.common.util.EMap;
/**
* Event Model functions for APP4MC's {@link PeriodicBurstStimulus}.
* <p>
* For a full documentation of the underlying functions, please refer to the
* <a href="https://doi.org/10.1016/j.sysarc.2021.102343">related
* publication</a>, Section 3.3.
*/
public class EMPeriodicBurst implements IEventModel {
/**
* Constant representing a Time object corresponding to 0ms.
*/
private static final Time ZEROMS = FactoryUtil.createTime("0ms");
/**
* The actual period of this trigger pattern
*/
private Time tOuterPeriod;
/**
* Duration of a single burst, i.e., the length of the time window in which
* activation events are released
*/
private Time tBurstLength;
/**
* Number of activation events triggered per burst
*/
private long iOccurrenceCount;
/**
* Minimum distance between two subsequent activation events
*/
private Time tMinDistance;
/**
* Internal offset by how much a time window in which activation events are
* released can be delayed
*/
private Time tDeltaBurst;
public EMPeriodicBurst() {
// Empty on purpose
}
@Override
public long etaPlus(final Time dt) {
// Return 0 on dt=0
if (EventModelFactory.isNullOrZero(dt)) {
// For dt = 0, the upper and lower bound functions return 0
return 0;
}
// Check for first case, tMin is unconstrained
if (EventModelFactory.isNullOrZero(this.tMinDistance)) {
final double dEtaScale = dt.add(this.tBurstLength).divide(this.tOuterPeriod);
return (long) (this.iOccurrenceCount * Math.ceil(dEtaScale));
}
// Second case, tMin is constrained
// EtaOuter
final double fraction = Math.floor(dt.add(this.tDeltaBurst).divide(this.tOuterPeriod));
final long iEtaOuter = (long) (this.iOccurrenceCount * fraction);
// EtaInner
// Check whether dt < TO - DeltaBurst
final long iEtaInner;
if (dt.compareTo(this.tOuterPeriod.subtract(this.tDeltaBurst)) < 0) {
// Return the minimum among b and ceil(dt/dmin)
iEtaInner = (long) Math.min(Math.ceil(dt.divide(this.tMinDistance)), this.iOccurrenceCount);
} else {
/*
* Calculate etaInner, i.e. the number of releases per "remainder" (dt mod
* T^Outer)
*/
// dt + db - T^Outer * floor((dt + db) /T^Outer)
// Calculate remainder (dt')
final Time rhs = this.tOuterPeriod.multiply(Math.floor(dt.add(this.tDeltaBurst).divide(this.tOuterPeriod)));
final Time tRemainder = dt.add(this.tDeltaBurst).subtract(rhs);
iEtaInner = (long) Math.min(Math.ceil(tRemainder.divide(this.tMinDistance)), this.iOccurrenceCount);
}
return iEtaOuter + iEtaInner;
}
@Override
public long etaMinus(final Time dt) {
// Return 0 on dt=0
if (EventModelFactory.isNullOrZero(dt)) {
// For dt = 0, the upper and lower bound functions return 0
return 0;
}
// Check for first case, tMin is unconstrained
if (EventModelFactory.isNullOrZero(this.tMinDistance)) {
final double dEtaScale = dt.subtract(this.tBurstLength).divide(this.tOuterPeriod);
return (long) Math.max(0, this.iOccurrenceCount * Math.floor(dEtaScale));
}
// Calculate dt'
// dt - (R + BL - 2(b-1)tMin) + 1
final Time dtOffsetRaw = dt
.subtract(this.tOuterPeriod.add(this.tBurstLength)
.subtract(this.tMinDistance.multiply(this.iOccurrenceCount - 1).multiply(2)))
.add(this.tMinDistance);
// Max(dtOffsetRaw, 0)
final Time dtOffset = (dtOffsetRaw.compareTo(ZEROMS) > 0) ? dtOffsetRaw : ZEROMS;
// etaOuter
final double fraction = Math.floor(dtOffset.divide(this.tOuterPeriod));
final long iEtaOuter = (long) (this.iOccurrenceCount * fraction);
/*
* Calculate etaInner, i.e. the number of releases per "remainder" (dt mod
* T^Outer)
*/
if (EventModelFactory.isNullOrZero(this.tMinDistance)) {
return iEtaOuter;
}
// dt - T^Outer * floor(dt/T^Outer)
final Time rhs = this.tOuterPeriod.multiply(Math.floor(dtOffset.divide(this.tOuterPeriod)));
final Time tRemainder = dtOffset.subtract(rhs);
final long iEtaInner = (long) Math.min(this.iOccurrenceCount, Math.ceil(tRemainder.divide(this.tMinDistance)));
return iEtaOuter + iEtaInner;
}
@Override
public Time deltaPlus(final long n) {
// Function is only valid for n >= 2, return null on invalid parameter
if (n < 2) {
return null;
}
// Integer division on purpose in order to round the result down
final long ceiledFraction = (n - 1) / this.iOccurrenceCount;
// Distance between full periods
final Time tFull = this.tOuterPeriod.multiply(ceiledFraction);
// Distance between remaining events
final Time tPartial;
final Time tSlack = this.tOuterPeriod.subtract(this.tMinDistance.multiply(this.iOccurrenceCount - 1));
if (tSlack.compareTo(this.tMinDistance) > 0) {
tPartial = this.tOuterPeriod.subtract(this.tMinDistance.multiply(this.iOccurrenceCount - (n - 1)));
} else {
tPartial = this.tMinDistance.multiply(n - 1);
}
return tFull.add(tPartial);
}
@Override
public Time deltaMinus(final long n) {
// Function is only valid for n >= 2, return null on invalid parameter
if (n < 2) {
return null;
}
// Integer division on purpose in order to round the result down
final long ceiledFraction = (n - 1) / this.iOccurrenceCount;
// Distance between full periods
final Time tFull = this.tOuterPeriod.multiply(ceiledFraction);
// Distance between remaining events
final long remainder = n - 1 - ceiledFraction * this.iOccurrenceCount;
final Time tPartial = this.tMinDistance.multiply(remainder);
return tFull.add(tPartial);
}
@Override
public double getUtilization(Process process, TimeType tt, EMap<ModeLabel, String> modes) {
// Note that bursts are considered to be strictly periodic in APP4MC.
// Accordingly, the outer period is assumed to be constant (without any drift)
// and the maximum number of activation events will be released with each burst.
// Therefore, the load is only influenced by a process's execution time, thus
// making it unnecessary to handle different cases of periods.
final Time executionTime = RuntimeUtil.getExecutionTimeForProcess(process, modes, tt);
return executionTime.multiply(getOccurrenceCount()).divide(getOuterPeriod());
}
public Time getOuterPeriod() {
return tOuterPeriod;
}
public void setOuterPeriod(Time tOuterPeriod) {
this.tOuterPeriod = tOuterPeriod;
}
public Time getBurstLength() {
return tBurstLength;
}
public void setBurstLength(Time tBurstLength) {
this.tBurstLength = tBurstLength;
}
public long getOccurrenceCount() {
return iOccurrenceCount;
}
public void setOccurrenceCount(long iOccurrenceCount) {
this.iOccurrenceCount = iOccurrenceCount;
}
public Time getMinDistance() {
return tMinDistance;
}
public void setMinDistance(Time tMinDistance) {
this.tMinDistance = tMinDistance;
}
public Time getDeltaBurst() {
return tDeltaBurst;
}
public void setDeltaBurst(Time tDeltaBurst) {
this.tDeltaBurst = tDeltaBurst;
}
}