| /** |
| ******************************************************************************** |
| * 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 java.math.BigInteger; |
| |
| import org.eclipse.app4mc.amalthea.model.ModeLabel; |
| import org.eclipse.app4mc.amalthea.model.PeriodicStimulus; |
| 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 PeriodicStimulus}. |
| * <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.1. |
| */ |
| public class EMPeriodic implements IEventModel { |
| /** |
| * The actual period of this trigger pattern |
| */ |
| private Time tOuterPeriod; |
| /** |
| * Minimum distance between two subsequent activation events |
| */ |
| private Time tMinDistance; |
| /** |
| * Maximum jitter by which an activation event can be delayed |
| */ |
| private Time tJitter; |
| |
| public EMPeriodic() { |
| // Empty on purpose |
| } |
| |
| @Override |
| public long etaPlus(final Time dt) { |
| // Return 0 on dt=0 |
| if (dt.getValue().compareTo(BigInteger.ZERO) == 0) { |
| return 0; |
| } |
| |
| // LHS: ceil(dt+J/T) |
| final Time num = dt.add(this.tJitter); |
| final double eta_plus_p = Math.ceil(num.divide(this.tOuterPeriod)); |
| |
| // RHS: ceil(dt/d) |
| final double eta_plus; |
| if (this.tMinDistance.getValue().compareTo(BigInteger.ZERO) != 0) { |
| final double eta_plus_d = Math.ceil(dt.divide(this.tMinDistance)); |
| eta_plus = Math.min(eta_plus_p, eta_plus_d); |
| } else { |
| eta_plus = eta_plus_p; |
| } |
| return (long) eta_plus; |
| } |
| |
| @Override |
| public long etaMinus(final Time dt) { |
| // Return 0 on dt=0 |
| if (dt.getValue().compareTo(BigInteger.ZERO) == 0) { |
| return 0; |
| } |
| |
| // LHS: floor(dt-J/T) |
| final Time num = dt.subtract(this.tJitter); |
| final double eta_min_p = Math.floor(num.divide(this.tOuterPeriod)); |
| |
| // RHS: floor(dt/d) |
| final double eta_min; |
| if (this.tMinDistance.getValue().compareTo(BigInteger.ZERO) != 0) { |
| final double eta_min_d = Math.floor(dt.divide(this.tMinDistance)); |
| eta_min = Math.max(eta_min_p, eta_min_d); |
| } else { |
| eta_min = eta_min_p; |
| } |
| |
| return (long) Math.max(0, eta_min); |
| } |
| |
| @Override |
| public Time deltaPlus(final long n) { |
| // Function is only valid for n >= 2, return null on invalid parameter |
| if (n < 2) { |
| return null; |
| } |
| |
| // Compare recurrence and minDistance, use the *highest* of both for |
| // further calculation |
| final Time tMinDistanceCalc = EventModelFactory.max(this.tOuterPeriod, this.tMinDistance); |
| |
| // Multiply this distance with the number of events (minus one) |
| final Time tDistance = tMinDistanceCalc.multiply(n - 1); |
| |
| return tDistance.add(this.tJitter); |
| |
| } |
| |
| @Override |
| public Time deltaMinus(final long n) { |
| // Function is only valid for n >= 2, return 0 on invalid parameter |
| if (n < 2) { |
| return FactoryUtil.createTime("0ms"); |
| } |
| |
| // Compare recurrence and minDistance, use the *highest* of both for |
| // further calculation |
| final Time tMinDistanceCalc = EventModelFactory.max(this.tOuterPeriod, this.tMinDistance); |
| |
| // Multiply this distance with the number of events (minus one) |
| final Time tDistance = tMinDistanceCalc.multiply(n - 1); |
| |
| /* |
| * Subtract jitter if and only if it does not decrease the distance between two |
| * activation events to less than tMinDistance. Cap it otherwise to the max. |
| * permitted value that does not violate tMinDistance. |
| */ |
| // Check if tRecurrence > tMinDistance (Prerequisite, otherwise jitter |
| // is capped at 0) |
| if (this.tOuterPeriod.compareTo(this.tMinDistance) > 0) { |
| final Time tMaxJitter; |
| final Time tDiffRecMinDistance = this.tOuterPeriod.subtract(this.tMinDistance); |
| // Check if Jitter exceeds (tRecurrence - tMinDistane) |
| if (this.tJitter.compareTo(tDiffRecMinDistance) > 0) { |
| // Yes, so max. Jitter must be capped at tRecurrence - |
| // tMinDistane |
| tMaxJitter = tDiffRecMinDistance; |
| } else { |
| // No, so max. Jitter can be taken as specified |
| tMaxJitter = this.tJitter; |
| } |
| return tDistance.subtract(tMaxJitter); |
| } |
| |
| return tDistance; |
| } |
| |
| @Override |
| public double getUtilization(Process process, TimeType tt, EMap<ModeLabel, String> modes) { |
| final Time time = RuntimeUtil.getExecutionTimeForProcess(process, modes, tt); |
| return time.divide(this.tOuterPeriod); |
| } |
| |
| public Time getRecurrence() { |
| return this.tOuterPeriod; |
| } |
| |
| public void setRecurrence(Time recurrence) { |
| this.tOuterPeriod = recurrence; |
| } |
| |
| public Time getMinDistance() { |
| return this.tMinDistance; |
| } |
| |
| public void setMinDistance(Time minDistance) { |
| this.tMinDistance = minDistance; |
| } |
| |
| public Time getJitter() { |
| return this.tJitter; |
| } |
| |
| public void setJitter(Time jitter) { |
| this.tJitter = jitter; |
| } |
| } |