6.3 The Flow Analysis Plug-in
The flow analysis plug-in described here can be found as the Eclipse/OSATE plug-in edu.cmu.sei.aadl.flowanalysis. The objective of this plug-in is to demonstrate
    • how flow specifications can be processed,
    • how flow specification validation and specification-based end-to-end flow analysis can be achieved by operating on the declarative AADL model,
    • and how analysis written for declarative models can be reused to operate on instance models.
First, we will focus on flow specification validation, and then on end-to-end flow analysis.
6.3.1 Meeting Flow Requirements
Flow specification declarations in component types can have properties that reflect expected flow characteristics that act as requirements. Flow implementations are expected to satisfy those requirements. Flow implementations are validated against the flow specification in terms of properties provided by the subcomponent flow specifications that are part of the flow implementation.
The AADL standard has pre-declared three latency related properties:
Latency: Time applies to (flow, connections);
The Latency property specifies the maximum amount of elapsed time allowed between the time the data or events enter the connection or flow and the time it exits.
Expected_Latency: Time applies to (flow);
The Expected_Latency property specifies the expected latency for a flow specification. Actual latency must not exceed the expected latency.
Actual_Latency: Time applies to (flow);
The Actual_Latency property specifies the actual latency as determined by the implementation of the end-to-end flow through semantic connections.
In our flow analysis plug-in we will make use of the Latency property and the Expected_Latency property. The Latency property associated with a flow specification is interpreted to represent the latency the component is expected to satisfy. The latency property associated with the flow implementation represents the latency determined by the connection latencies and the subcomponent flow specification latencies. The Expected_Latency property is used on end-to-end flows to indicate the desired latency, while the Latency property of the end-to-end flow represents the calculated latency determined by the connection latencies and the subcomponent flow specification latencies.
The Actual_Latency property can be used to recursively propagate latency information up the system hierarchy.
The flow specification validation is implemented by checking that every flow implementation satisfies the corresponding flow specification properties. This is achieved by traversing the declarative AADL model and processing through the caseFlowImpl method. This method handles all three subclasses of flow implementations: flow source implementations, flow sink implementations, and flow path implementations.
The code is shown in below. We iterate over all flow elements that make up the flow implementation and add up the latency values of the connections and subcomponent flow specifications that we encounter. We determine whether we are dealing with a connection or a subcomponent flow specification by testing for isConnectionReference(). The AADL front- end has already made sure that the flow element sequence alternates between connection references and subcomponent flow spec references, therefore we do not have to track which element we processed last. We do all the arithmetic by retrieving the latency values with respect to a specific unit, in our case in micro seconds. We take advantage of the predeclared property definitions available in the edu.cmu.sei.aadl.property.predeclared package. Once the result is added up we store the result as Latency property value with the flow implementation and compare it to the Latency property of the flow specification. The property value is set by creating a new IntegerValue object, setting its value and measurement unit, and then performing a setPropertyValue(PredeclaredProperties.LATENCY,newpv); on the flow implementation object.
public Object caseFlowImpl(FlowImpl fi) {
FlowSpec fs = fi.getXImplement();
EList fel = fi.getFlowElement();
double result = 0;
for (Iterator it = fel.iterator(); it.hasNext();){
FlowElement fe = (FlowElement)it.next();
if (fe.isConnectionReference()){
Connection conn = fe.getConnection();
IntegerValue cpv = (IntegerValue)conn.getSimplePropertyValue(
PredeclaredProperties.LATENCY);
if (cpv != null) {
result = result +
cpv.getScaledValue(PredeclaredProperties.MICROSEC);
}
} else {
FlowSpec fefs = fe.getFlowSpec();
if (fefs != null){
IntegerValue fefspv =
(IntegerValue) fefs.getSimplePropertyValue(
PredeclaredProperties.LATENCY);
if (fefspv != null) {
result = result +
fefspv.getScaledValue(PredeclaredProperties.MICROSEC);
} else {
// handle the case when no latency is specified
}
}
// store the result as flow implementation latency
IntegerValue newpv =
PropertyFactory.eINSTANCE.createIntegerValue();
newpv.setNewValue((long) result);
newpv.setUnitLiteral(PredeclaredProperties.MICROSEC);
fi.setPropertyValue(PredeclaredProperties.LATENCY,newpv);
// now we compare the result to the latency in the flow spec
IntegerValue fspv = (IntegerValue) fs.getSimplePropertyValue(
PredeclaredProperties.LATENCY);
if (fspv != null) {
double fslv = fspv.getScaledValue(
PredeclaredProperties.MICROSEC);
if (result > fslv) {
reportError(fi, "Flow implementation latency " +
result + "exceeds flow spec latency " + fslv);
}
}
6.3.2 Handling of Missing Latency Properties
This basic analysis algorithm can be refined in several ways.
First, we deal with the case when a subcomponent flow specification does not have a Latency property value and the subcomponent is a thread. In this case we can infer the worst case latency from the thread characteristics. If the thread is a periodic thread and the incoming connection is a delayed connection, then the period of the thread determines the latency contribution of the thread. If the thread is periodic and the incoming connection is an immediate connection or the thread is an aperiodic or sporadic thread then the latency contribution is the completion time, whose worst case is the thread deadline.
We add code to track whether the incoming connection was a delayed data connection. We do that every time we encounter a connection reference by checking the timing attribute of the DataConnection.
boolean wasDelayedConnection = false;
for (Iterator it = fel.iterator(); it.hasNext();) {
FlowElement fe = (FlowElement) it.next();
if (fe.isConnectionReference()) {
Connection conn = fe.getConnection();
if (conn instanceof DataConnection &&
((DataConnection)conn).getTiming() ==
ConnectionTiming.DELAYED_LITERAL) {
wasDelayedConnection = true;
} else {
wasDelayedConnection = false;
}
IntegerValue cpv = (IntegerValue) conn.getSimplePropertyValue(
PredeclaredProperties.LATENCY);
Then we deal with the case when a Latency property value could not be found and the subcomponent is a thread subcomponent. In that case we retrieve the dispatch protocol from the thread classifier. We use the getClassifier() method that is defined for the ThreadSubcomponent to get the classifier. If the thread is periodic and the previous (incoming) connection was delayed we retrieve the period and add it, otherwise we retrieve the deadline and add it.
} else {
Subcomponent sc = fe.getFlowContext();
if (sc instanceof ThreadSubcomponent){
ThreadClassifier tc = ((ThreadSubcomponent) sc).getClassifier();
EnumLiteral dp = tc.getDispatchProtocol();
if (dp == PredeclaredProperties.PERIODIC && wasDelayedConnection) {
IntegerValue period = (IntegerValue) sc.getSimplePropertyValue(
PredeclaredProperties.PERIOD);
if (period != null){
result = result +
period.getScaledValue(PredeclaredProperties.MICROSEC);
} else {
reportInfo(sc, "Thread subcomponent has no flowspec latency " +
"or periodic thread period");
}
} else {
IntegerValue deadline = (IntegerValue) sc.getSimplePropertyValue(
PredeclaredProperties.DEADLINE);
if (deadline != null){
result = result+deadline.getValue();
} else {
reportInfo(sc, "Thread subcomponent has no flowspec latency " +
" or thread deadline");
}
}
} else {
reportInfo(sc, sc.getComponentType().getCategory().getName() +
" subcomponent has no flowspec latency for flow " +
fefs.getName());
}
}
6.3.3 Propagation of Flow Latency Information
The second refinement is to record the flow implementation result as the flow specification latency if the flow specification does not have such a property value. If we do this and traverse the AADL model according to the use hierarchy of component implementations we will ensure that flow specification latency property values have been filled in before they are used. We accomplish this by adding a caseComponentImpl method that is invoked by the processBottomUpComponentImpl() method. This traversal method visits only component implementations and does so bottom up by processing component implementation classifiers before they are referenced by subcomponent declarations. This traversal method is invoked by the analysis action method doAaxlAction. The caseComponentImpl method retrieves the list of flow implementations and invoke the processElist method for switch-based processing. Instead, we simply could have invoked a second traversal method to process its content according to the case methods defined for the flow analysis.
// inside the caseFlowImpl method
// set the sum value for the flow implementation
IntegerValue newpv = PropertyFactory.eINSTANCE.createIntegerValue();
newpv.setNewValue((long)result);
newpv.setUnitLiteral(PredeclaredProperties.MICROSEC);
fi.setPropertyValue(PredeclaredProperties.LATENCY,newpv);
// now we compare the result to the latency in the flow spec
IntegerValue fspv = (IntegerValue)fs.getSimplePropertyValue(
PredeclaredProperties.LATENCY);
if (fspv != null){
public Object caseComponentImpl(ComponentImpl ci) {
self.processEList(ci.getFlowSequence());
// alternative: self.processPreOrderAll(ci);
return DONE;
}

public void doAaxlAction(AObject obj){
AObject root = obj.getAObjectRoot();
AadlProcessingSwitch flowLatencySwitch =
new FlowLatencyAnalysisSwitch(getMarkerReporter());
if (root instanceof AadlSpec) {
flowLatencySwitch.processBottomUpComponentImpl((AadlSpec)root);
6.3.4 Reusing the Analysis for Instance Models
Finally, we refine the plug-in to allow the use of the analysis methods on instance models as well. This is feasible for analyses that can produce results based on processing of declarative AADL models. In this case we will traverse the AADL instance model in prefix order, i.e., bottom up in terms of the instance hierarchy, by using the processPreOrderComponentInstance method. For each component instance we encounter we simply delegate the processing to the appropriate component implementation of the subcomponent that was instantiated as component instance. This is done with the caseComponentInstance method and for the system instance object with the caseSystemInstance method. The caseComponentInstance method checks for non-null component implementation classifiers; this is done to handle instance models with subcomponent classifiers of leaf component instances being component type only.
public void doAaxlAction(AObject obj){
AObject root = obj.getAObjectRoot();
AadlProcessingSwitch flowLatencySwitch =
new FlowLatencyAnalysisSwitch(getMarkerReporter());
if (root instanceof AadlSpec){
flowLatencySwitch.processBottomUpComponentImpl((AadlSpec)root);
} else {
flowLatencySwitch.processPreOrderComponentInstance(
(SystemInstance) root);
}
reportDone(root,"Flow Latency Checking");
}

public Object caseComponentInstance(ComponentInstance ci) {
Subcomponent sub = ci.getSubcomponent();
ComponentImpl cii = sub.getComponentImpl();
if (cii == null) return DONE;
self.processPreOrderAll(cii);
return DONE;
}
public Object caseSystemInstance(SystemInstance ci) {
SystemImpl si = ci.getSystemImpl();
self.processPreOrderAll(si);
return DONE;
}