BusLoadAnalysis.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.resource.budgets.logic;
import java.util.Iterator;
import java.util.function.Function;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.osate.aadl2.ComponentCategory;
import org.osate.aadl2.Element;
import org.osate.aadl2.UnitLiteral;
import org.osate.aadl2.contrib.aadlproject.SizeUnits;
import org.osate.aadl2.contrib.util.AadlContribUtils;
import org.osate.aadl2.instance.ComponentInstance;
import org.osate.aadl2.instance.ConnectionInstance;
import org.osate.aadl2.instance.ConnectionInstanceEnd;
import org.osate.aadl2.instance.FeatureInstance;
import org.osate.aadl2.instance.InstanceObject;
import org.osate.aadl2.instance.SystemInstance;
import org.osate.aadl2.instance.SystemOperationMode;
import org.osate.aadl2.modelsupport.modeltraversal.ForAllElement;
import org.osate.aadl2.modelsupport.modeltraversal.SOMIterator;
import org.osate.aadl2.util.Aadl2Util;
import org.osate.analysis.resource.budgets.busload.NewBusLoadAnalysis;
import org.osate.ui.dialogs.Dialog;
import org.osate.ui.handlers.AbstractAaxlHandler;
import org.osate.xtext.aadl2.properties.util.AadlProject;
import org.osate.xtext.aadl2.properties.util.GetProperties;
import org.osate.xtext.aadl2.properties.util.InstanceModelUtil;
/**
* @since 2.0
* @deprecated Use {@link NewBusLoadAnalysis} instead. Will be removed in 2.8.
*/
@Deprecated
public class BusLoadAnalysis extends AbstractLoggingAnalysis {
private final String actionName;
public BusLoadAnalysis(final String actionName, final AbstractAaxlHandler errManager) {
super(errManager);
this.actionName = actionName;
}
public void analysisBody(final IProgressMonitor monitor, final Element obj) {
if (obj instanceof InstanceObject) {
SystemInstance root = ((InstanceObject) obj).getSystemInstance();
monitor.beginTask(actionName, IProgressMonitor.UNKNOWN);
final SOMIterator soms = new SOMIterator(root);
while (soms.hasNext()) {
final SystemOperationMode som = soms.nextSOM();
checkBusLoads(root, som);
}
monitor.done();
} else {
Dialog.showError("Bound Bus Bandwidth Analysis Error", "Can only check system instances");
}
}
private void checkBusLoads(SystemInstance si, final SystemOperationMode som) {
errManager.infoSummaryReportOnly(si, null, "\nBus Summary Report: " + Aadl2Util.getPrintableSOMName(som));
ForAllElement mal = new ForAllElement() {
@Override
protected void process(final Element obj) {
final ComponentInstance ci = (ComponentInstance) obj;
final ComponentCategory cat = ci.getCategory();
if (cat == ComponentCategory.BUS) {
checkBusBandWidthLoad(ci, som);
} else if (cat == ComponentCategory.VIRTUAL_BUS) {
checkVirtualBusBandWidthLoad(ci, som);
}
}
};
mal.processPreOrderComponentInstance(si);
}
private void checkBusBandWidthLoad(final ComponentInstance curBus, SystemOperationMode som) {
checkBandWidthLoad(curBus, som, b -> GetProperties.getBandWidthCapacityInKBytesps(b, 0.0), "bus", "capacity");
}
private void checkVirtualBusBandWidthLoad(final ComponentInstance curBus, SystemOperationMode som) {
checkBandWidthLoad(curBus, som, b -> GetProperties.getBandWidthBudgetInKBytesps(b, 0.0), "virtual bus",
"budget");
}
/**
* check the load from connections bound to the given bus
*
* @param curBus Component Instance of bus
* @param doBindings if true do bindings to all buses, if false do them only
* for EtherSwitch
* @param somName String somName to be used in messages
*/
private void checkBandWidthLoad(final ComponentInstance curBus, SystemOperationMode som,
Function<ComponentInstance, Double> getCapacityOrBudget, String busLabel, String capacityOrBudgetLabel) {
UnitLiteral kbspsliteral = GetProperties.getKBytespsUnitLiteral(curBus);
double busCapacityOrBudget = getCapacityOrBudget.apply(curBus);
boolean doBroadcast = GetProperties.isBroadcastProtocol(curBus);
double totalBandWidth = 0.0;
EList<ConnectionInstance> budgetedConnections = InstanceModelUtil.getBoundConnections(curBus);
EList<ComponentInstance> budgetedVBs = InstanceModelUtil.getBoundVirtualBuses(curBus);
// filters out to use only Port connections or feature group connections
// it also tries to be smart about not double accounting for budgets on FG that now show for every port instance inside.
budgetedConnections = filterInMode(budgetedConnections, som);
if (doBroadcast) {
budgetedConnections = filterSameSourceConnections(budgetedConnections);
}
budgetedVBs = filterInMode(budgetedVBs, som);
if (busCapacityOrBudget == 0) {
if (!budgetedConnections.isEmpty() || !budgetedVBs.isEmpty()) {
errManager.errorSummary(curBus, null,
" " + curBus.getComponentInstancePath() + " has no " + capacityOrBudgetLabel
+ " but has bound connections or virtual buses");
} else {
errManager.warningSummary(curBus, null,
" " + curBus.getComponentInstancePath() + " has no " + capacityOrBudgetLabel
+ " and no demand");
}
return;
}
if (budgetedConnections.isEmpty() && budgetedVBs.isEmpty()) {
errManager.infoSummary(curBus, null,
" " + curBus.getComponentInstancePath() + " with bandwidth " + capacityOrBudgetLabel + " "
+ busCapacityOrBudget + " " + kbspsliteral.getName() + " has no bound connections");
return;
}
if (Aadl2Util.isNoModes(som)) {
errManager.logInfo("\n\nBudget Details for " + busLabel + " " + curBus.getFullName() + " with "
+ capacityOrBudgetLabel + " "
+ busCapacityOrBudget + " " + kbspsliteral.getName());
} else {
errManager.logInfo(
"\n\nBudget Details for " + busLabel + " " + curBus.getFullName()
+ Aadl2Util.getPrintableSOMName(som)
+ " with " + capacityOrBudgetLabel + " " + busCapacityOrBudget + " "
+ kbspsliteral.getName());
}
if (budgetedConnections.size() > 0) {
errManager.logInfo("\nConnection,Budget,Actual (Data Size * Sender Rate),Note");
for (ConnectionInstance connectionInstance : budgetedConnections) {
double budget = 0.0;
double actual = 0.0;
if ((!connectionInstance.getSource().isActive(som))
|| (!connectionInstance.getDestination().isActive(som))) {
continue;
}
// we have a binding, is it to the current bus
budget = GetProperties.getBandWidthBudgetInKBytesps(connectionInstance, 0.0);
actual = calcBandwidthKBytesps(connectionInstance.getSource());
String note = "";
if (budget > 0) {
if ((actual > 0) && (actual > budget)) {
totalBandWidth += actual;
note = "Actual bandwidth exceeds bandwidth budget. Using actual";
} else {
totalBandWidth += budget;
note = "Using budget bandwidth";
}
} else {
if (actual > 0) {
totalBandWidth += actual;
note = "No bandwidth budget. Using actual";
} else {
note = "No bandwidth budget or actual bandwidth from port data size&rate";
}
}
detailedLog(connectionInstance, budget, actual, note);
}
}
if (budgetedVBs.size() > 0) {
errManager.logInfo("\nVirtual Bus,Budget,Note");
for (ComponentInstance componentInstance : budgetedVBs) {
double budget = 0.0;
// we have a binding, is it to the current bus
budget = GetProperties.getBandWidthBudgetInKBytesps(componentInstance, 0.0);
if (budget > 0) {
totalBandWidth += budget;
}
detailedLog(componentInstance, budget, "See separate virtual bus entry");
}
}
detailedLogTotal2(null, totalBandWidth, SizeUnits.KBYTE);
if (totalBandWidth > busCapacityOrBudget) {
errManager.errorSummary(curBus, null,
curBus.getComponentInstancePath() + " bandwidth capacity " + busCapacityOrBudget + " "
+ kbspsliteral.getName() + " exceeded by connection bandwidth budget totals "
+ totalBandWidth + " " + kbspsliteral.getName());
} else if (totalBandWidth > 0.0) {
errManager.infoSummary(curBus, null,
curBus.getComponentInstancePath() + " bandwidth capacity " + busCapacityOrBudget + " "
+ kbspsliteral.getName() + " sufficient for connection bandwidth budget totals "
+ totalBandWidth + " " + kbspsliteral.getName());
} else {
errManager.warningSummary(curBus, null, curBus.getComponentInstancePath() + " bandwidth capacity "
+ busCapacityOrBudget + " " + kbspsliteral.getName() + " and bound connections without bandwidth budget");
}
}
private <E extends InstanceObject> EList<E> filterInMode(EList<E> instanceObjects,
SystemOperationMode som) {
EList<E> result = new BasicEList<E>();
for (E io : instanceObjects) {
if (io.isActive(som)) {
result.add(io);
}
}
return result;
}
private EList<ConnectionInstance> filterSameSourceConnections(EList<ConnectionInstance> connections) {
EList<ConnectionInstance> result = new BasicEList<ConnectionInstance>();
for (ConnectionInstance conni : connections) {
if (!hasConnectionSource(result, conni)) {
result.add(conni);
}
}
return result;
}
/**
* Calculate bandwidth demand from rate & data size
*
* @param pci Port connection instance
* @return
*/
private double calcBandwidthKBytesps(ConnectionInstanceEnd cie) {
double res = 0;
// TODO-LW add other cases
if (cie instanceof FeatureInstance) {
FeatureInstance fi = (FeatureInstance) cie;
double datasize = AadlContribUtils.getDataSize(fi, SizeUnits.KBYTE);
double srcRate = GetProperties.getOutgoingMessageRatePerSecond(fi);
res = datasize * srcRate;
EList<FeatureInstance> fil = fi.getFeatureInstances();
if (fil.size() > 0) {
double subres = 0;
for (Iterator<FeatureInstance> it = fil.iterator(); it.hasNext();) {
FeatureInstance sfi = it.next();
subres = subres + calcBandwidthKBytesps(sfi);
}
if (subres > res) {
if (res > 0) {
errManager.warningSummary(fi, null, "Bandwidth of feature group ports " + subres
+ " exceeds feature group bandwidth " + res);
}
res = subres;
}
}
}
return res;
}
private boolean hasConnectionSource(EList<ConnectionInstance> connections, ConnectionInstance conni) {
ConnectionInstanceEnd src = conni.getSource();
for (ConnectionInstance connectionInstance : connections) {
if (connectionInstance.getSource() == src) {
return true;
}
}
return false;
}
/**
* @since 3.0
*/
protected void detailedLog(InstanceObject obj, double budget, String msg) {
String budgetmsg = budget + " " + AadlProject.KBYTESPS_LITERAL + ",";
String objname = (obj instanceof ConnectionInstance) ? obj.getFullName()
: ((ComponentInstance) obj).getComponentInstancePath();
errManager.logInfo(objname + ", " + budgetmsg + msg);
}
}