Closed stevevestal closed 3 years ago
For the records, the following issue mentioned the legality rule in https://github.com/saeaadl/aadlv2.1/issues/15, and implemented in OSATE in this commit: https://github.com/osate/osate2/commit/3c3293495bcda30fdc6c1689059ebf28d79ab8f6
From your use cases,
Use case#1 could be done by flows, defining dependencies between inputs and outputs; TSS routing are defined this way in the FACE Annex. Is that a concern?
Use case#2 depends on the granularity of your system. This is a topic for the network annex for which scalability is a concern.
A flow is not a connection. Use case #1 allows instantiation of a connection between an out port of a source component, through the intermediate template abstraction, to the in port of a destination component. This instantiated semantic connection can then have connection properties associated with it, can be bound to a routing through a network, etc. There are analyses that work on connections distinct from flows.
The errata to 2.1 I mentioned was to address an incorrect interpretation of connections that would contradict the definition from 9 (1).
"9 (1) A connection is a linkage between features of two components that represents communication of data and control between components."
"10 (1) or there exists a flow path through the component, i.e., from one of its incoming ports to one of its outgoing ports."
Your request would basically break this definition for a very specific scenario.
In addition; your first use case was a modeling issue; now you turn it into an analysis issue. Please provide a full scenario for the use case, partial information does not help to understand your global concern. Is the modeling pattern from FACE Annex I mentioned not sufficient? Should it be revisited?
Finally, your analysis concern could be addressed by revisiting model processing. Ocarina had an extension that computed all possible end-to-end flows from the set of connections and flow paths in a system. Building extended semantic connections by merging disjoint semantic connections that have a common component is an alternative that does not break the initial concept of connection in the language.
Excuse the lengthy response, but you have raised an issue of interest to me.
First, here are the two paragraphs you cited. To me, they don't make a distinction that I find useful and significant.
9 (1) A connection is a linkage between features of two components that represents communication of data and control between components. This can be the transmission of control and data between ports of different threads or between threads and processor or device components. A connection may denote an event that triggers a mode transition. The timing of data and control transmission depends on the connection category and on the dispatch protocol of the connected threads. The flow of data between parameters of subprogram calls within a thread may be specified using connections. Finally, connections designate access to shared components.
10 (1) A flow is a logical flow of data and control through a sequence of threads, processors, devices, and port connections or data access connections. A component can have a flow specification, which specifies whether a component is a flow source, i.e., the flow starts within the component, a flow sink, i.e., the flow ends within the component, or there exists a flow path through the component, i.e., from one of its incoming ports to one of its outgoing ports.
To me, an important distinction is that a connection specifies something in the as-built system that allows information to pass from one component to another. In contrast, a Flow declaration identifies one among many possible information flows though a system. A Flow declaration is illegal according to the standard if, for example, there does not exist a connection or suitable category of component that indicates an actual transmission or transfer of information can occur. A Flow declaration in and of itself does not specify information transfer behaviors in the as-built system that were not already specified by other elements of the model. Those behaviors have to be there already, else the Flow declaration is not legal. Removing all Flow declarations from a model would not change the possible transmissions or transfers of information declared in the model for the as-built system, although that would greatly affect the operation of many analysis tools.
For example, Flow or error propagation declarations are not needed to do error analysis. Many years ago, I worked a project that built tools that generated fault tree and stochastic concurrent process models that did not look at Flow declarations at all. Those tools took into account all possible ways that information might propagate through a system based on all the declarations in a model that specified ways information could be transferred or transmitted (and ways error propagations could be blocked/masked at the boundaries of fault containment zones).
Connections are only one such mechanism. Putting two threads in the same address space, or binding them to a processor that does not use a scheduling discipline that enforces acceptable time isolation, are other mechanisms that permit information exchanges (in a "may" rather than "must" sense). The current Flow declarations do not account for these other information transfer mechanisms, although error analysis tools should. Cybersecurity is another example where all possible mechanisms by which information may flow is not the same thing as a modest number of Flow declarations that identify a modest number of specific paths that information flows through selected elements in the model.
A final example is a distinction I would make when doing schedulability analysis. We use Flow declarations the way we use Deadline properties. They specify timing requirements that are to be met for designated thread and connection declarations. Schedulability analysis looks at properties like thread Period and processor Scheduling_Protocol to determine behavior of the as-built system (assuming the model accurately describes the as-built system). Analysis determines bounds on response times to be verified against the timing requirements. Schedulability analysis computes end-to-end response times along transfer/transmission sequences identified by Flow declarations, which are checked against Latency requirements for this or that information flow of interest. (Exactly how to think of Deadline, Latency, etc. when generating schedules or configuring enforcement mechanisms might be another interesting topic for discussion.)
To be somewhat inconsistent with myself, in an earlier errata I noted that the standard seemed to require that every Flow path declared in an AADL type must have a Flow path implementation declared in every AADL implementation of that type. That is, the sequence of actual transfer/transmission mechanisms that make that Flow path declaration legal must be present in the model and identified in the combined hierarchy of Flow declarations. You can declare a Flow through a component itself rather than a path of that component, and as I recall this means all possible paths that information could possibly flow between the input and output ports identified by the connections (the connections always being essential). I asked that requirement be relaxed. I actually don't mind imposing it at instantiation time, I just wanted to defer that legality rule so that it could be satisfied by subsequent extensions of an implementation, since refinement of Flow implementation declarations is restrictive.
So I would like to be able to declare a connection from an in port of a component to an out port of a component to specify that there shall exist an implemented mechanism in the as-built system to effect a transfer of information. In a fully detailed model (noting that a big strength of AADL is the ability to do abstract and mixed-fidelity modeling), a Flow path declaration is not legal unless the information transfer behaviors are declared, and the as-built system is defective if it does not satisfy those model-based specifications.
The issue mentioned above is saeaadl/aadlv2.2#41
Lengthy rebuttal are fine, and they allow to keep the memory of the discussion. So I will never object.
I think one source of divergence is on the term flow. It is used in a generic way. But 10 (1) is really about end-to-end flow.
I always distinguish
Now, let us consider this example that illustrates this flow spec as a design contract as I intend it:
package Errata_55
public
abstract foo
features
inp: in event port;
outp: out event port;
flows
f1: flow path inp -> outp;
end foo;
abstract implementation foo.impl
subcomponents
bar: abstract foo;
connections
C1: port inp -> bar.inp;
C2: port bar.outp -> outp;
end foo.impl;
system baz
end baz;
system implementation baz.impl
subcomponents
f: abstract foo.impl;
g: abstract foo.impl;
connections
C1: port f.outp -> g.inp;
C2: port g.outp -> f.inp;
end baz.impl;
end Errata_55;
foo has a contract that mentions inp is linked to outp. I use a flow path for that. this flow path "exists" before I introduce its supporting elements in the implementation. Later on, the implementation is providing one such way to support this dependency.
So when you wrote "Those behaviors have to be there already, else the Flow declaration is not legal.", i disagree, a flow path can be defined without specifying the supporting elements. But we agree on the fact that flows can be removed without changing the behavior of the system.
We may revisit the legality rules to decide whether or not we need a flow implementation, or a consistency rule that the connection patterns should match the flow spec. Let us save this for issue saeaadl/aadlv2.2#41.
So a flow path may act as a specification mechanism, the same way in your use case#1, this flow path might be compatible with an implementation that adds intermediate components. There are situations where we want to discuss legality or consistency rules, but this is fine
Now, let us imagine this, which is (I hope) a transcription of your workflow:
abstract foo
features
inp: in event port;
outp: out event port;
end foo;
abstract implementation foo.zero
connections
C1: port inp -> outp; -- illegal today, but desired
end foo.zero;
abstract implementation foo.impl extends foo.zero
subcomponents
bar: abstract foo;
connections
-- How do I refine C1 above ? how can I say C1 becomes
-- C1a : inp -> bar.inp + C1b: bar.outp ->outp
end foo.impl;
I define C1 in an empty abstract implementation to relate input to output. Then, how do I refine it? I cannot with the current syntax "split C1" into a flow path from inp through bar to outp to substantiate the requirement that "there shall exist an implemented mechanism in the as-built system to effect a transfer of information." If we were to take the path of refinements, we must refine C1 and break it into pieces to add intermediate processing. How to achieve that? or do you want to bind this connection to another connection?
Your last paragraph makes me thing that we could clarify the legality rules so that the example I built is prescriptive enough. You wrote "a Flow path declaration is not legal unless the information transfer behaviors are declared, and the as-built system is defective if it does not satisfy those model-based specifications.", and I agree. But this can be done in two phases as shown above.
In the first example, we may want to flag during instanciation any missing architectural elements (connections, subcomponents) that do not support a flow specification and/or mandate a flow implementation. In other words, shall we support connections as you propose? or clarify some usage patterns of flows and associated legality rules?
I would prefer the second option as it could be supported without major changes to the language and its meta-model. In addition, it seems compatible with your requirement of defining a contract that you later implement.
I like that idea of thinking of a flow specification as a kind of contract. Case#1 is a bit of a corner case, but we are having to deal with it in practice. To echo your final sentence, what if I want to fulfill a flow path contract for a component by passing the messages directly from component input to component output? I want to avoid introducing intermediate components altogether. How would I declare that?
Steve. Let me stop here. You do not provide a working example with the initial component category that would host this pattern, and associated components that would use it. I have other matters to complete. I will resume when you provide a working example.
The example you gave is a working example, repeated below with some name changes. This is pretty much something that will come from generating AADL from a FACE integration model. Another use case is a system created for the explicit purpose of varying connection routings, pick among implementations to pick a desired switching of inputs to outputs. The inability to refine such a connection or flow later is a fundamental limitation of the current AADL standard, not limited to these specific use cases. Ticket #41 was intended to alleviate that some. There are cases where one would like to refine a connection or flow in an extension to introduce a new intermediate element (not the topic of this issue, but an example of the bigger issue with weak connection and flow refinement features).
abstract TransferNode features inp: in event port; outp: out event port; end TransferNode;
abstract implementation TransferNode.NoTypeConversion connections C1: port inp -> outp; -- illegal today, but desired end TransferNode.NoTypeConversion;
As I indicated above, there are some existing definitions for flows and connections, and consistency must be preserved. You should provide proper definition for connections to illustrate what you want; so that we can evaluate their impact.
For instance, in your example, I can have a connection between A and TransfertNode bound to a bus, C1 (in your example) bound to another bus. What is the semantics here? since ultimately we would have one semantic connection between two components bound to two buses by virtue of composition. So, we should review this first.
There is a current limit in AADL today in refining subcomponents, you can refine only one subcomponent, i.e. A.B.C.D.E.F : refined to < >; is not possible
That being said, I understand what you want to achieve with TransferNode.NoTypeConversion, but you will be hit by the "refined to" limit, which means that (pseudo syntax below)
system S1.configured subcomponents A, B : system foo; TransferNode : abstract TransferNode.NoTypeConversion; end S1.configured;
could be refined into
system S1.configuredv2 extends S1.configured subcomponents TransferNode : refined to abstract TransferNode.somethingelse; end S1.configuredv2;
but if S1.configuredv2 is itself subcomponent of a bigger system, you will have to recompose a new hierarchy. You have a limited gain until we address this refined to thing. For the moment, we left it for v3
but this is not different from
system S1.to_be_configured subcomponents A, B: system foo; end S1.to_be_configured;
system S1.configured extends S1.to_be_configured connections .... end S1.configured;
system S1.configuredv2 extends S1.to_be_configured subcomponents -- whatever additional stuff you want here connections .... end S1.configured;
In your desired approach, the unit of configuration is a subcomponent you can replace, with the risk of twisting the definition of connections.
In my proposed workaround, you achieve similar capability by configuring a set of subcomponents while keeping it compatible with the current core. I understand that if you have multiple TransfertNodes; this could be problematic, but this becomes a model configuration issue. v3 had some discussions that I will not repeat here that could prove to be useful.
So, for as long as I understand the intent, the modifications require far more exploration and a full example
No action for v2.3, consider for future revision
Legality rule 9 (L4) says A connection declaration must not connect an incoming feature to an outgoing feature of the same components.
This is somewhat ambiguous. Does it mean the following is what is disallowed?
system implementation Foo.Impl subcomponent x : process HasFeatures_A_And_B; connections forbidden1: feature x.B -> x.A; forbidden2: feature x.A -> x.B;
Or does it mean that if type Foo has features A and B, then the following is what is forbidden:
system implementation Foo.Bar connections forbidden1: feature B -> A; forbidden2: feature A -> B;
Or does it mean both?
What was the original rationale for making this restriction?
In any case, there may be circumstances where this is useful. The second case means that a component could be defined that simply passes one of its inputs to one of its outputs.
Use case: A connection inside a reusable implementation template is routed through an abstract component in anticipation of a need to modify message representations. When that implementation is refined, in most cases the message does not need to be converted. It is convenient to refine that abstract component so it simply passes its input to its output. Forcing a thread or data object to appear in the middle of this connection can be awkward and does not always represent the desired semantics.
Use case: Insert a "switching" component as a variation point in a product line model, where alternative implementations (selected by extension and refinement) are basically just different patterns of making certain connections. Connections can be refined with different in_modes_and_transitions clauses, but the semantics desired here is really alternative design-time configurations, not alternative run-time configurations. One could simply define multiple implementations of the original implementation-with-variations, but those multiple implementations might have a lot in common (so be less concise and maintainable). It's not clear which approach is more scalable in large models with nested components that have variation points. This use case is complicated, it would take some experimenting to judge.