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