LatencyContributor.java
/**
* Copyright (c) 2004-2025 Carnegie Mellon University and others. (see Contributors file).
* All Rights Reserved.
*
* NO WARRANTY. ALL MATERIAL IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY
* KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE
* OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT
* MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
*
* 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
*
* Created, in part, with funding and support from the United States Government. (see Acknowledgments file).
*
* This program includes and/or can make use of certain third party source code, object code, documentation and other
* files ("Third Party Software"). The Third Party Software that is used by this program is dependent upon your system
* configuration. By using this program, You agree to comply with any and all relevant Third Party Software terms and
* conditions contained in any such Third Party Software or separate license file distributed with such Third Party
* Software. The parties who own the Third Party Software ("Third Party Licensors") are intended third party benefici-
* aries to this license with respect to the terms applicable to their Third Party Software. Third Party Software li-
* censes only apply to the Third Party Software and not any other portion of this program or this program as a whole.
*/
package org.osate.analysis.flows.model;
import static org.osate.result.util.ResultUtil.addRealValue;
import static org.osate.result.util.ResultUtil.addStringValue;
import static org.osate.result.util.ResultUtil.createDiagnostic;
import java.util.ArrayList;
import java.util.List;
import org.osate.aadl2.NamedElement;
import org.osate.aadl2.instance.ConnectionInstance;
import org.osate.aadl2.instance.InstanceObject;
import org.osate.analysis.flows.internal.utils.FlowLatencyUtil;
import org.osate.analysis.flows.reporting.model.Line;
import org.osate.analysis.flows.reporting.model.ReportSeverity;
import org.osate.result.Diagnostic;
import org.osate.result.DiagnosticType;
import org.osate.result.Result;
import org.osate.result.ResultFactory;
/**
* A latency Result represents something in the flow
* that can contribute to increase/decrease the latency.
*
* This class contains the result for a latency contributor
* with min/max latency.
*
*
*/
public abstract class LatencyContributor {
// used for processing/communication latency
// UNKNOWN: method not set (default)
// Processing_Time: processing (compute execution) time
// Deadline: worst-case (assumes schedulability) if no processing time
// Transmission time: actual transmission latency
// Specified: latency specified with flow specification if no processing/transmission time
// Sampling latency contributions when recipient operates periodically
// Sampled: sampling latency of period by periodic recipient or transfer mechanism
// Delayed: frame-delay latency due to incoming delayed connection
// Immediate: incoming immediate connection and outgoing immediate connection (acts like an aperiodic event/msg
// driven task).
// Last_Immediate: last of incoming immediate connection: Its deadline determines the deadline of the sequence.
// Queued: latency contribution due to queuing on bus or on recipient queuing ports
// Partition frame: Major frame rate of partition
// Partition schedule: frame offset
// Partition IO: I/O delay to partition window end or major frame
public enum LatencyContributorMethod {
UNKNOWN, DEADLINE,
/**
* @since 3.0
*/
RESPONSE_TIME, PROCESSING_TIME, DELAYED, SAMPLED, FIRST_PERIODIC, SPECIFIED, QUEUED, TRANSMISSION_TIME, PARTITION_FRAME, PARTITION_SCHEDULE, PARTITION_OUTPUT, SAMPLED_PROTOCOL
};
/**
* The relatedElement represents the AADL element that
* is related to this latency contributor. Mostly, it is
* a component or a connection.
*/
protected NamedElement relatedElement;
/**
* This represents the max and min value of the latency
* for this contributor.
*/
private double minValue;
private double maxValue;
/**
* The expected minimum and maximum latency values.
*/
private double expectedMin;
private double expectedMax;
/**
* Hold on to deadline for LAST_IMMEDIATE
*/
private double immediateDeadline;
/**
* Sampling offset for partition frame
*/
private double partitionOffset;
/**
* Partition window length
*/
private double partitionDuration;
/**
* Sampling period for SAMPLED, DELAYED, or partition related
*/
private double samplingPeriod;
List<Diagnostic> issues;
/**
* Sampling of incoming communication is synchronous
* Set if model indicates so. The doSynchronous value is examined if this is not set.
*/
public enum SynchronizeType {
ASYNCHRONOUS, SYNCHRONOUS, SYNCUNKNOWN
};
private SynchronizeType isSynchronized = SynchronizeType.SYNCUNKNOWN;
/**
* methods represent what is the model elements used
* to compute the min or max value
*/
private LatencyContributorMethod worstCaseMethod;
private LatencyContributorMethod bestCaseMethod;
/**
* The sub contributors are basically what are the other
* elements that can incur a latency in addition to the
* related element. A good example is a bus for a
* connection. The connection is the latency contributor
* and the bus is a sub-contributor (it adds potentially
* some latency).
*/
private List<LatencyContributor> subContributors;
private final boolean majorFrameDelay;
public LatencyContributor(boolean majorFrameDelay) {
this.worstCaseMethod = LatencyContributorMethod.UNKNOWN;
this.bestCaseMethod = LatencyContributorMethod.UNKNOWN;
this.isSynchronized = SynchronizeType.SYNCUNKNOWN;
this.minValue = 0.0;
this.maxValue = 0.0;
this.expectedMax = 0.0;
this.expectedMin = 0.0;
this.immediateDeadline = 0.0;
this.samplingPeriod = 0.0;
this.partitionOffset = 0.0;
this.partitionDuration = 0.0;
this.subContributors = new ArrayList<LatencyContributor>();
this.issues = new ArrayList<Diagnostic>();
this.majorFrameDelay = majorFrameDelay;
}
protected List<Diagnostic> getReportedIssues() {
return this.issues;
}
public void reportError(String str) {
issues.add(createDiagnostic(str, this.relatedElement, DiagnosticType.ERROR));
}
public void reportInfo(String str) {
issues.add(createDiagnostic(str, this.relatedElement, DiagnosticType.INFO));
}
public void reportWarning(String str) {
issues.add(createDiagnostic(str, this.relatedElement, DiagnosticType.WARNING));
}
public void reportError(boolean doMaximum, String str) {
issues.add(createDiagnostic(FlowLatencyUtil.getMinMaxLabel(doMaximum) + str,
this.relatedElement, DiagnosticType.ERROR));
}
public void reportInfo(boolean doMaximum, String str) {
issues.add(createDiagnostic(FlowLatencyUtil.getMinMaxLabel(doMaximum) + str,
this.relatedElement, DiagnosticType.INFO));
}
public void reportWarning(boolean doMaximum, String str) {
issues.add(createDiagnostic(FlowLatencyUtil.getMinMaxLabel(doMaximum) + str,
this.relatedElement, DiagnosticType.WARNING));
}
public void reportErrorOnce(boolean doMaximum, String str) {
if (doMaximum) {
return;
}
reportError(str);
}
public void reportInfoOnce(boolean doMaximum, String str) {
if (doMaximum) {
return;
}
reportInfo(str);
}
public void reportWarningOnce(boolean doMaximum, String str) {
if (doMaximum) {
return;
}
reportWarning(str);
}
protected String getContributorName() {
return relatedElement.getName();
}
protected NamedElement getContributor() {
return relatedElement;
}
protected String getFullComponentContributorName() {
if (this instanceof LatencyContributorComponent) {
if (relatedElement instanceof InstanceObject) {
return ((InstanceObject) relatedElement).getComponentInstancePath();
}
return relatedElement.getQualifiedName();
} else if (this instanceof LatencyContributorConnection) {
return relatedElement.getQualifiedName();
}
return "";
}
/**
* @since 4.0
*/
protected abstract String getFlowSpecName();
protected abstract String getContributorType();
public void setSynchronous() {
this.isSynchronized = SynchronizeType.SYNCHRONOUS;
}
public void setAsynchronous() {
this.isSynchronized = SynchronizeType.ASYNCHRONOUS;
}
public void setSyncUnknown() {
this.isSynchronized = SynchronizeType.SYNCUNKNOWN;
}
public boolean isSynchronous() {
return this.isSynchronized.equals(SynchronizeType.SYNCHRONOUS);
}
public boolean isAsynchronous() {
return this.isSynchronized.equals(SynchronizeType.ASYNCHRONOUS);
}
public boolean isSyncUnknown() {
return this.isSynchronized.equals(SynchronizeType.SYNCUNKNOWN);
}
public double getSamplingPeriod() {
return this.samplingPeriod;
}
public void setSamplingPeriod(double val) {
this.samplingPeriod = val;
}
public double getPartitionOffset() {
return this.partitionOffset;
}
public void setPartitionOffset(double val) {
this.partitionOffset = val;
}
public double getPartitionDuration() {
return this.partitionDuration;
}
public void setPartitionDuration(double val) {
this.partitionDuration = val;
}
public double getImmediateDeadline() {
return this.immediateDeadline;
}
public void setImmediateDeadline(double val) {
this.immediateDeadline = val;
}
public void setExpectedMaximum(double d) {
this.expectedMax = d;
}
public void setExpectedMinimum(double d) {
this.expectedMin = d;
}
public LatencyContributorMethod getWorstcaseLatencyContributorMethod() {
return this.worstCaseMethod;
}
public LatencyContributorMethod getBestcaseLatencyContributorMethod() {
return this.bestCaseMethod;
}
public LatencyContributorMethod getLatencyContributorMethod(boolean doWorstcase) {
if (doWorstcase) {
return this.worstCaseMethod;
} else {
return this.bestCaseMethod;
}
}
// UNKNOWN, DEADLINE, PROCESSING_TIME, DELAYED, SAMPLED, FIRST_SAMPLED, SPECIFIED, QUEUED, TRANSMISSION_TIME, PARTITION_FRAME, PARTITION_SCHEDULE, PARTITION_IO
public String mapMethodToString(LatencyContributorMethod method) {
switch (method) {
case DEADLINE:
return "deadline";
case RESPONSE_TIME:
return "response time";
case PROCESSING_TIME:
return "processing time";
case DELAYED:
return "delayed sampling";
case SPECIFIED:
return "specified";
case SAMPLED:
return "sampling";
case FIRST_PERIODIC:
return "first sampling";
case QUEUED:
return "queued";
case PARTITION_FRAME:
return "partition major frame";
case PARTITION_SCHEDULE:
return "partition offset";
case TRANSMISSION_TIME:
return "transmission time";
case PARTITION_OUTPUT:
return "partition output" + (majorFrameDelay ? " (MF)" : " (PE)");
case SAMPLED_PROTOCOL:
return "sampling protocol/bus";
case UNKNOWN:
return "no sampling/queuing latency";
default:
return "no latency";
}
}
public void setWorstCaseMethod(LatencyContributorMethod m) {
this.worstCaseMethod = m;
}
public void setBestCaseMethod(LatencyContributorMethod m) {
this.bestCaseMethod = m;
}
public List<LatencyContributor> getSubContributors() {
return this.subContributors;
}
public void addSubContributor(LatencyContributor lc) {
this.subContributors.add(lc);
}
public void setMinimum(double d) {
this.minValue = d;
}
public void setMaximum(double d) {
this.maxValue = d;
}
public void setActualValue(double d, boolean doMaximum) {
if (doMaximum) {
this.setMaximum(d);
} else {
this.setMinimum(d);
}
}
public double getLocalMinimum() {
return this.minValue;
}
public double getLocalMaximum() {
return this.maxValue;
}
public double getTotal(boolean doMaximum) {
if (doMaximum) {
return getTotalMaximum();
} else {
return getTotalMinimum();
}
}
public double getTotalMinimum() {
double res = this.minValue;
for (LatencyContributor lc : subContributors) {
res = res + lc.getTotalMinimum();
}
return res;
}
public double getTotalMaximum() {
double res = this.maxValue;
for (LatencyContributor lc : subContributors) {
res = res + lc.getTotalMaximum();
}
return res;
}
public double getTotalMinimumSpecified() {
double spec = this.expectedMin;
double res = 0;
for (LatencyContributor lc : subContributors) {
res = res + lc.getTotalMinimumSpecified();
}
if (this.relatedElement instanceof ConnectionInstance) {
// we compare the subtotals against own
if (spec > 0 && res > spec) {
reportWarning("specified min protocol latency subtotal " + res + " exceeds connection latency " + spec);
} else {
if (spec > 0) {
reportInfo("Using specified min protocol latency subtotal " + res
+ " although specified connection latency " + spec + " is greater");
}
}
} else {
// we add own to subtotals
res = res + spec;
}
return res;
}
public double getTotalMaximumSpecified() {
double spec = this.expectedMax;
double res = 0;
for (LatencyContributor lc : subContributors) {
res = res + lc.getTotalMaximumSpecified();
}
if (this.relatedElement instanceof ConnectionInstance) {
// we compare the subtotals against own
if (spec > 0 && res > spec) {
reportWarning("specified max protocol latency subtotal " + res + " exceeds connection latency " + spec);
} else {
if (spec > 0) {
reportInfo("Using max specified protocol latency subtotal " + res
+ " although specified connection latency " + spec + " is greater");
}
}
} else {
// we add own to subtotals
res = res + spec;
}
return res;
}
public boolean isPartition() {
return worstCaseMethod.equals(LatencyContributorMethod.PARTITION_FRAME)
|| worstCaseMethod.equals(LatencyContributorMethod.PARTITION_SCHEDULE);
}
public boolean isPartitionFrame() {
return worstCaseMethod.equals(LatencyContributorMethod.PARTITION_FRAME);
}
public boolean isPartitionOffset() {
return worstCaseMethod.equals(LatencyContributorMethod.PARTITION_SCHEDULE);
}
public boolean isPartitionOutputDelay() {
return worstCaseMethod.equals(LatencyContributorMethod.PARTITION_OUTPUT);
}
public boolean isSamplingContributor() {
return isPartition() || isSamplingTask();
}
public boolean isSamplingTask() {
return worstCaseMethod.equals(LatencyContributorMethod.SAMPLED)
|| worstCaseMethod.equals(LatencyContributorMethod.FIRST_PERIODIC)
|| worstCaseMethod.equals(LatencyContributorMethod.DELAYED);
}
public void checkConsistency() {
if ((this.expectedMax != 0.0) && (this.maxValue > this.expectedMax)) {
reportWarning(true, "actual latency exceeds max flow latency");
}
if ((this.expectedMin != 0.0) && (this.minValue > this.expectedMin)) {
reportWarning(false, "actual latency exceeds min flow latency");
}
}
public Result genResult() {
Result result = ResultFactory.eINSTANCE.createResult();
result.setModelElement(relatedElement);
result.getDiagnostics().addAll(issues);
addRealValue(result, minValue);
addRealValue(result, maxValue);
addRealValue(result, expectedMin);
addRealValue(result, expectedMax);
addStringValue(result, mapMethodToString(bestCaseMethod));
addStringValue(result, mapMethodToString(worstCaseMethod));
addStringValue(result, getFlowSpecName());
/**
* We also add the lines of all the sub-contributors.
*/
for (LatencyContributor lc : this.subContributors) {
result.getSubResults().add(lc.genResult());
}
return result;
}
public List<Line> export() {
return export(0);
}
private String levelOpenLabel(int level) {
if (level > 0) {
return "(";
}
return "";
}
private String levelCloseLabel(int level) {
if (level > 0) {
return ")";
}
return "";
}
public List<Line> export(int level) {
List<Line> lines;
Line myLine;
lines = new ArrayList<Line>();
/**
* We also add the lines of all the sub-contributors.
*/
for (LatencyContributor lc : this.subContributors) {
lines.addAll(lc.export(level + 1));
}
myLine = new Line();
myLine.setSeverity(ReportSeverity.INFO);
myLine.addContent(levelOpenLabel(level) + this.getContributorType() + " "
+ this.getFullComponentContributorName() + " " + getFlowSpecName() + levelCloseLabel(level));
if (this.expectedMin != 0.0) {
myLine.addContent(this.expectedMin + "ms");
} else {
myLine.addContent(""); // the min expected value
}
myLine.addContent(this.getTotalMinimum() + "ms");
myLine.addContent(mapMethodToString(bestCaseMethod));
if (this.expectedMax != 0.0) {
myLine.addContent(this.expectedMax + "ms");
} else {
myLine.addContent(""); // the min expected value
}
myLine.addContent(this.getTotalMaximum() + "ms");
myLine.addContent(mapMethodToString(worstCaseMethod));
myLine.addCells(this.getReportedIssues());
lines.add(myLine);
return lines;
}
}