![]() ![]() |
||||
|
||||
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
First,
we will focus on flow specification validation, and then on end-to-end flow analysis. |
||||
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);
}
} |
||||
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());
}
} |
||||
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); |
||||
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;
} |
||||