encapsulation, operator overloading, and error class...
TRANSCRIPT
OCL @ MODELS 2016
Encapsulation, Operator Overloading,
and Error Class Mechanisms in OCL
Vincent Bertram, Bernhard Rumpe,
Michael von Wenckstern
Software Engineering
RWTH Aachen
http://www.se-rwth.de/
Vincent Bertram
Software Engineering
Chair (Bernhard Rumpe)
RWTH Aachen University
Page 2
Outline
Encapsulation, Operator Overloading, Error Classes2.
Example Workflow3.
Conclusion4.
Motivation1.
Vincent Bertram
Software Engineering
Chair (Bernhard Rumpe)
RWTH Aachen University
Page 3
Motivation - General
Due to the highly competitive automotive market, manufacturers
update their vehicles continuously with new features.
A special single feature does not necessary affect every software
part, therefore individual components are updated to successively
replace old component versions with new ones.
A feature change can cause incompatibilities, the automotive
industry is constantly stating the structural (and/or behavioral)
backward compatibility.
Vincent Bertram
Software Engineering
Chair (Bernhard Rumpe)
RWTH Aachen University
Page 4
Motivation – Static software verification
Static software verification is a software engineering discipline,
analyzing software against a given specification without running any
line of code by using formal methods.
Checking models for correctness or compatibility using standard
formal modeling techniques (such as OCL) has merits in abstraction
and compactness.
It is inconvenient for developers, since there are no standard
mechanisms how to handle large and complex OCL constraints.
Vincent Bertram
Software Engineering
Chair (Bernhard Rumpe)
RWTH Aachen University
Page 5
Motivation - Questions
The contribution is to answer the following questions:
(1) How to logically group OCL constraints?
(2) How to split up complex constraints easily into multiple smaller
ones?
(3) How to use OCL operators for self-defined model structures?
(4) How to produce meaningful error messages to the user?
Vincent Bertram
Software Engineering
Chair (Bernhard Rumpe)
RWTH Aachen University
Page 6
Outline
Encapsulation, Operator Overloading, Error Classes2.
Example Workflow3.
Conclusion4.
Motivation1.
Vincent Bertram
Software Engineering
Chair (Bernhard Rumpe)
RWTH Aachen University
Page 7
Interface compatibility (A is interface compatible to B iff)
• Component A has at least (it may have more) the same input and
output port names as Component B
• A’s input ports accept the same or more input values than B’s
input ports
• A’s output ports produce the same or fewer output values than
B’s output ports
It is not complicated to define interface compatibility, but still, you
can face a lot of small constraints:
• (1) primitive data type compatibility (e.g. when are enumerations
compatible)
• (2) unit compatibility such as km/h and m/s
• (3) range compatibility considering several ranges each of which
may have different min, max, accuracy or resolution
Interface compatibility
Vincent Bertram
Software Engineering
Chair (Bernhard Rumpe)
RWTH Aachen University
Page 8
Derived MetaModel
Bertram, V., Manhart, P., Plotnikov, D., Rumpe, B., Schulze, C., von Wenckstern, M.: Infrastructure to Use OCL for Runtime Structural Compatibility Checks of Simulink Models. In: Modellierung (2016)
dest
CD …
FunctionComponent
«interface»
FunctionComponentElement
PortConnector
1contains
Interface
String: name
implements
«abstract»
Port
source
String: name
Boolean: in
1
1String: name
1..*
res
min
max
1
1
1
acc
«in
sta
nce
Of»
Number
Primitive
Type
Range Value
0..1
PrimitiveType
Reference
«interface»
Unit
type
ranges
QuantityKind
String: name
1
1
1
0..1
accuracy
resolution
is implemented byconcrete SI units(kg, m, s)
(class diagram is an excerpt)
*
11..*
* *
*
* **
*
*
*
0..1
*
acceleration, energy or speed
Vincent Bertram
Software Engineering
Chair (Bernhard Rumpe)
RWTH Aachen University
Page 9
ADAS Object Diagram instantiation
ADAS_V1: FunctionComponent
v_LimiterSetValue:
SignalPort
in = true
:PrimitiveType
Reference
KilometerPerHour:
DerivedUnit
:Range
min = 0
max = 250
:Resolution
value = 0.05
Acceleration_pedal_pc:
SignalPort
in = true
:Accuracy
value= 3.5
res
ADAS_V4: FunctionComponent
v_LimiterSetValue:
SignalPort
in = true
:PrimitiveType
Reference
None:
DerivedUnit
:Range
min = 0
max = 250
:Resolution
value = 0.05
Acceleration_pedal_pc:
SignalPort
in = true
:Accuracy
value= 3.0
res
OD …OD …
Interface backward compatibility between two Component & Connector
(C&C) models. Given if ADAS_V4 can replace ADAS_V1.
Vincent Bertram
Software Engineering
Chair (Bernhard Rumpe)
RWTH Aachen University
Page 10
General interface compatibility constraint
InterfaceCompatibility
CD
«query» + Boolean v2BackwardsCompatibleToV1()
FunctionComponent
String: name
v1 v2
1context InterfaceCompatibility ic inv:
2 ic.v2BackwardsCompatibleToV1()
3 <=> …
OCL/P…
1context InterfaceCompatibility inv:
2 self.v2BackwardsCompatibleToV1()
3 = …
OCL 2.4 …
The constraint has a context and
describes an invariant, meaning the
constraint must be satisfied for all
InterfaceCompatiblity class instances.
the equals operator in OCL 2.4 is similar to OCL/P’s <=>.
(ADAS_V1)
describes a property that must hold in a system at each point of time
the context in which a constraint is embedded into; e.g. names of classes, attributes and/or properties in class diagrams
a boolean statement about a system
(ADAS_V4)
calls for every InterfaceCompatiblity object the query method v2BackwardsCompatibleToV1().
Vincent Bertram
Software Engineering
Chair (Bernhard Rumpe)
RWTH Aachen University
Page 11
Definition of Library Expressions
Defining an OCL library with expressions in a function-like way
The definition expression defines new attributes and query
operations to existing models, which can be used in other
constraints. Define a new query method: IsBackwardsCompatibleTo(FunctionComponent v1) to the
CD model FunctionComponent and makes the modeling of an extra class like InterfaceCompatibility in the CD redundant.
This function depends on other compatibility constraints such as
data type, range, unit compatibility. This would result in polluting CD
with unnecessary, and probably not reusable, query functions just to
make one definition good readable.
• We present a method for how to define an OCL library
comfortably with public (can be called from outside the library)
and private (can only be used inside this library) functions
Vincent Bertram
Software Engineering
Chair (Bernhard Rumpe)
RWTH Aachen University
Page 12
Library definition in OCL/P and OCL
1library InterfaceCompatiblity is:
2 + def boolean v2BackwardsCompatibleToV1
3 (FunctionComponent v1, FunctionComponent v2) is:
4 result =
5 (forall Port ports1 in v1.implements.ports,
6 Port ports2 in v2.implements.ports:
7 dataTypeCompatible(ports1, ports2))
8 && …
9 - def boolean dataTypeCompatible(Port p1, Port p2) is:
10 result = …
public
OCL/P…
private
11package InterfaceCompatiblity
12 context FunctionComponent
13 def: v2BackwardsCompatibleToV1(v2: FunctionComponent)
14 :Boolean =
15 self.implements.ports->forAll(ports1: Port |
16 v2.implements.ports->forall(ports2: Port |
17 dataTypeCompatible(ports1, ports2))
18 && …
19 context Port
20 def: dataTypeCompatible(p2: Port) :Boolean = …
21endpackage
OCL 2.4 …
Example for an OCL library with public and private query functions
Vincent Bertram
Software Engineering
Chair (Bernhard Rumpe)
RWTH Aachen University
Page 13
Define new functions and operators
OCL/P
OCL/P
OCL 2.4
semantically equivalent
1context FunctionComponent v1:
2 def boolean v2BackwardsCompatibleToV1(FunctionComponent v2) is:
1def boolean v2BackwardsCompatibleToV1
2 (FunctionComponent v1, FunctionComponent v2) is:
there exists no equivalent translation in OCL 2.4 (“Definition constraint must be attached to a Classifier“)
1context FunctionComponent
2 def: v2BackwardsCompatibleToV1(v2: FunctionComponent) :Boolean =
The top part shows the previous convenience definition.
FunctionComponent extending the class FunctionComponent with an extra
query function v2BackwardsCompatibleToV1().
Similar to C ++ ’s mechanism that define new functions and operators as
member and non-member, new operations can also be defined without an
explicit given context.
Vincent Bertram
Software Engineering
Chair (Bernhard Rumpe)
RWTH Aachen University
Page 15
Overload infix / prefix operators using OCL/P
1def boolean infix (Unit u1) ~ (Unit u2) is:
2 result = u1.quantityKind == u2.quantityKind
3def boolean infix (Number v) in (Range r) is:
4 result =
5 v >= r.min &&
6 v <= r.max &&
7 (~r.res || (v - r.min) % r.res == 0)
8def boolean infix (Number v) in (List<Range> ranges) is:
9 result = exists Range r in ranges: v in r
10def boolean typeReferenceCompatible(PrimitiveTypeReference tR1,
11 PrimitiveTypeReference tR2) is:
12 let
13 PrimitiveTypeReference tR1c = tR1.convert(tR2.unit)
14 in
15 result =
16 tR1.unit ~ tR2.unit &&
17 forall Number v in tR1c.ranges:
18 v in tR2.ranges &&
19 …
(converts e.g. tR1 from eg. 1 m in 100 cm)
(Range r has no optional association res to Resolution)
OCL/P…
20def boolean prefix ~ (Association a) is:
21 result = a.size > 0
syntax of overloading a prefix operator
Vincent Bertram
Software Engineering
Chair (Bernhard Rumpe)
RWTH Aachen University
Page 16
Overload operators using OCL
1context Unit
2 def: _’=’(u2: Unit) :Boolean = self.quantityKind == u2.quantityKind
3context Number
4 def: isInRange(r: Range) :Boolean =
5 self >= r.min &&
6 self <= r.max &&
7 (r.res->notEmpty() || (self - r.min) % r.res == 0)
8 def: isInOneRange(ranges: Sequence(Range)) :Boolean =
9 ranges->exists(r: Range | self.isInRange(r))
10context PrimitiveTypeReference
11 def: typeReferenceCompatible(tR2: PrimitiveTypeReference) :Boolean =
12 let
13 tR1c: PrimitiveTypeReference = tR1.convert(tR2.unit)
14 in
16 tR1.unit = tR2.unit &&
17 Number.allInstances()->forall(v: Number |
18 v.isInOneRange(tR1c.ranges) implies
19 v.isInOneRange(tR2.ranges) &&
20 …
OCL 2.4 …
changed from ‘~’ to ‘=’, because OCL 2.4 can only overlaod:‘+’, ‘-’, ‘*’, ‘/’, ‘=’, ‘<>’, ‘<’, ‘>’, ‘<=’, ‘>=’
Vincent Bertram
Software Engineering
Chair (Bernhard Rumpe)
RWTH Aachen University
Page 17
OCL Error Classes for Intuitive Feedback
A mechanism how to generate user-friendly error messages if OCL
constraints fail. These error messages can be domain-specific and
hence can give users all the information needed to trace down
existing errors.
One drawback of a OCL definition is its restriction to a Boolean
result for the user. The answer satisfied (ADAS_V4 is compatible to
ADAS_V1) or non-satisfied makes it hard to understand where
exactly the constraints failed in case of a negative answer.
A definition of error classes overcomes this drawback by providing
easy to understand witness instantiations of an OCL error class.
Vincent Bertram
Software Engineering
Chair (Bernhard Rumpe)
RWTH Aachen University
Page 18
Example error class instantiation
A valid UnitWitness instantiation
related to the previos OD would have
the attribute values:
portName = "v_LimiterSetValue"
unit1 = "KilometerPerHour“
unit2 = "None"
A witness instance can be used in templates for generating user friendly
text messages. In order to maintain only one kind of artifact, OCL has
been extended by the counterexample keyword as demonstrated:
counterexample String portName, Unit unit1, Unit unit2 inv UnitWitness:
«query»
UnitWitness
- readonly String portName
- readonly Unit unit1
- readonly Unit unit2
CD
+ String getPortName()
+ Unit getUnit1()
+ Unit getUnit2()
Vincent Bertram
Software Engineering
Chair (Bernhard Rumpe)
RWTH Aachen University
Page 19
Defining error classes producing counterexamples
1context UnitWitness uw inv:
2 let
3 SignalPort p1 = uw.getUnit1().
4 primitiveTypeReference.signalPort;
5 SignalPort p2 = uw.getUnit2().
6 primitiveTypeReference.signalPort;
7 in
8 p1.interface.name != p2.interface.name &&
9 uw.getPortName() == p1.name &&
10 uw.getPortName() == p2.name &&
11 !(uw.getUnit1() ~ uw.getUnit2())
OCL/P…
1counterexample String portName, Unit unit1, Unit unit2 inv UnitWitness:
2 let
3 SignalPort p1 = unit1.primitiveTypeReference.signalPort;
4 SignalPort p2 = unit2.primitiveTypeReference.signalPort;
5 in
6 p1.interface.name != p2.interface.name &&
7 portName == p1.name && portName == p2.name &&
8 !(unit1 ~ unit2)
OCL/P…
OCL/P can navigate againstnavigation direction
ports belong to different function components (different interface names)
ports have the same name
units of the ports are not compatible
«query»
UnitWitness- readonly String portName
- readonly Unit unit1
- readonly Unit unit2
CD
CD + OCL/P context is replaced by
OCL/P counterexample
+ String getPortName()
+ Unit getUnit1()
+ Unit getUnit2()
semantically equivalent
Vincent Bertram
Software Engineering
Chair (Bernhard Rumpe)
RWTH Aachen University
Page 20
OCL Conditions to define Error Classes
B1
B1
CO1
R1
R2
R8
R11
…
All names of model elements within a component namespace have to be unique.
Top-level component type definitions do not have instance names.
Connectors may not pierce through component interfaces.
Each outgoing port of a component type definition is used at most once as target
of a connector.
Each incoming port of a subcomponent is used at most once as target of a
connector.
The target port in a connection has to be compatible to the source port, i.e., the
type of the target port is identical or a supertype of the source port type.
Inheritance cycles of component types are forbidden.
For each formalized ContextCondition also a counterexample class is
given to produce meaningful user feedback.
Vincent Bertram
Software Engineering
Chair (Bernhard Rumpe)
RWTH Aachen University
Page 21
Outline
Encapsulation, Operator Overloading, Error Classes2.
Example Workflow3.
Conclusion4.
Motivation1.
Vincent Bertram
Software Engineering
Chair (Bernhard Rumpe)
RWTH Aachen University
Page 22
Check structural compatibility
Compatibility
statement
Two white box
Simulink
models
[no structural
compatibility]Return not
matching
ports
Check
interface
compatibility
AD
➀
➁ ➂
[structural
compatibility]
Workflow: Check structural compatibility
Create
counter
example
ADAS_V1
ADAS_V4
④
➄
④
➄
➀
Vincent Bertram
Software Engineering
Chair (Bernhard Rumpe)
RWTH Aachen University
Page 23
classdef(Enumeration) LeverAngle <
Simulink.IntEnumType
enumeration
ZeroDegree(0)
PlusFiveDegree(1)
MinusFiveDegree(2)
end
end
LeverAngle.m
(SPa)
(SPb)
(VP)
(SPc)(CP)
(TP)
(1)
(2)
(3)
(SPa)
classdef(Enumeration) LeverAngle <
Simulink.IntEnumType
enumeration
ZeroDegree(0)
PlusFiveDegree(1)
PlusSevenDegree(2)
MinusFiveDegree(3)
MinusSevenDegree(4)
end
end
LeverAnglePro.m
ADAS main component
Vincent Bertram
Software Engineering
Chair (Bernhard Rumpe)
RWTH Aachen University
Page 24
«enumeration»
LeverAnglePro
ZeroDegree
PlusFiveDegree
PlusSevenDegree
MinusFiveDegree
MinusSevenDegree
«enumeration»
LeverAngle
ZeroDegree
PlusFiveDegree
MinusFiveDegree
CD
CD
SL
Data type compatibility fault of type enumeration
Vincent Bertram
Software Engineering
Chair (Bernhard Rumpe)
RWTH Aachen University
Page 25
Simulink: structural incompatibility
Structural compatibility errors found (left) are illustrated as Simulink model (right)
Vincent Bertram
Software Engineering
Chair (Bernhard Rumpe)
RWTH Aachen University
Page 26
Outline
Encapsulation, Operator Overloading, Error Classes2.
Example Workflow3.
Conclusion4.
Motivation1.
Vincent Bertram
Software Engineering
Chair (Bernhard Rumpe)
RWTH Aachen University
Page 27
Conclusion (Concepts)
The OCL library concept with private and public constraints, supports
to organize the amount of constraints by hiding unnecessary details
for developers.
Maybe operator overloading would not be necessary, but it made it
easier to read OCL constraints.
The most important concept is the introduction of error classes, other-
wise it is not possible to use OCL for ContextConditions, since the
user needs to know which C&C element causes a constraint to fail.
To create even better user feedback, error classes are prioritized.
The error prioritization is done by defining disjunct error classes,
which ensures that solely one error is causing the incompatibility for
exactly one port of the C&C model instead of many different errors
that are implied by the one main error (e.g. unit compatibility is higher
prioritized than range compatibility).
making a unit dimensionless results in changing its value ranges and its accuracies
Vincent Bertram
Software Engineering
Chair (Bernhard Rumpe)
RWTH Aachen University
Page 28
Conclusion (Answering the initial questions)
How to logically group OCL constraints?
Create OCL libraries to structure the code and use their encapsulation
mechanisms with private and public constraint definitions.
How to split up complex constraints easily into multiple smaller ones?
Create smaller OCL helper constraints by the OCL def operator. It is now very
similar to splitting large Java or C function into smaller ones.
How to use OCL operators for self-defined model structures?
Thanks to operator overloading, a well-known principle in many languages, self-
defined models, e.g. complex numbers defined as a CD, can be accessed as
intuitive (e.g. + operator) as the OCL basic types such as integer numbers.
How to produce meaningful user error messages?
A methodology on how to specify and prioritize error classes for users to
generate intuitive user feedback was developed.
Vincent Bertram
Software Engineering
Chair (Bernhard Rumpe)
RWTH Aachen University
Page 29
Finish
Thank you for your attention.