pg-verify 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +98 -0
- data/README.md +29 -0
- data/Rakefile +62 -0
- data/bin/console +15 -0
- data/bin/pg-verify.rb +18 -0
- data/bin/setup +8 -0
- data/calc.ebnf +21 -0
- data/data/config/pg-verify.yml +66 -0
- data/data/nusmv.sample.smv +179 -0
- data/data/project-template/.gitignore.resource +4 -0
- data/data/project-template/.pg-verify.yml +0 -0
- data/data/project-template/README.md +18 -0
- data/data/project-template/addon/.keep +0 -0
- data/data/project-template/program-graph.rb.resource +103 -0
- data/devpg +5 -0
- data/doc/examples/railroad_crossing.rb +61 -0
- data/doc/examples/train-tree.rb +43 -0
- data/doc/examples/weidezaun.rb +99 -0
- data/doc/examples/weidezaun.txt +29 -0
- data/doc/expose/definition.png +0 -0
- data/doc/expose/diagram.png +0 -0
- data/doc/expose/expose.md +359 -0
- data/doc/expose/validity.png +0 -0
- data/exe/pg-verify +4 -0
- data/integration_tests/ruby_dsl/001_states.rb +10 -0
- data/integration_tests/ruby_dsl/002_transitions.rb +10 -0
- data/integration_tests/ruby_dsl/003_actions.rb +14 -0
- data/integration_tests/ruby_dsl/004_guards.rb +18 -0
- data/integration_tests/ruby_dsl/005_variables.rb +16 -0
- data/integration_tests/ruby_dsl/006_state_variables.rb +26 -0
- data/integration_tests/ruby_dsl/007_variable_initialization.rb +28 -0
- data/integration_tests/ruby_dsl/008_state_initialization.rb +19 -0
- data/integration_tests/ruby_dsl/009_shared_variables.rb +26 -0
- data/integration_tests/ruby_dsl/010_complex_guards.rb +18 -0
- data/integration_tests/ruby_dsl/011_complex_actions.rb +16 -0
- data/integration_tests/ruby_dsl/012_error_components.rb +9 -0
- data/integration_tests/ruby_dsl/013_hazards.rb +25 -0
- data/integration_tests/ruby_dsl/014_tau_transitions.rb +26 -0
- data/integration_tests/ruby_dsl/015_basic_dcca.rb +19 -0
- data/integration_tests/ruby_dsl/016_pressure_tank.rb +146 -0
- data/lib/pg-verify/cli/cli.rb +235 -0
- data/lib/pg-verify/core/cmd_runner.rb +151 -0
- data/lib/pg-verify/core/core.rb +38 -0
- data/lib/pg-verify/core/extensions/array_extensions.rb +11 -0
- data/lib/pg-verify/core/extensions/enumerable_extensions.rb +19 -0
- data/lib/pg-verify/core/extensions/nil_extensions.rb +7 -0
- data/lib/pg-verify/core/extensions/string_extensions.rb +84 -0
- data/lib/pg-verify/core/shell/colorizer.rb +136 -0
- data/lib/pg-verify/core/shell/shell.rb +0 -0
- data/lib/pg-verify/core/util.rb +146 -0
- data/lib/pg-verify/doctor/doctor.rb +180 -0
- data/lib/pg-verify/ebnf_parser/ast.rb +31 -0
- data/lib/pg-verify/ebnf_parser/ebnf_parser.rb +26 -0
- data/lib/pg-verify/ebnf_parser/expression_parser.rb +177 -0
- data/lib/pg-verify/ebnf_parser/expression_parser2.rb +422 -0
- data/lib/pg-verify/ebnf_parser/expressions.ebnf +33 -0
- data/lib/pg-verify/ebnf_parser/expressions.peg +52 -0
- data/lib/pg-verify/ebnf_parser/parser_result.rb +26 -0
- data/lib/pg-verify/interpret/component_context.rb +125 -0
- data/lib/pg-verify/interpret/graph_context.rb +85 -0
- data/lib/pg-verify/interpret/interpret.rb +142 -0
- data/lib/pg-verify/interpret/pg_script.rb +72 -0
- data/lib/pg-verify/interpret/spec/ltl_builder.rb +90 -0
- data/lib/pg-verify/interpret/spec/spec_context.rb +32 -0
- data/lib/pg-verify/interpret/spec/spec_set_context.rb +67 -0
- data/lib/pg-verify/interpret/transition_context.rb +55 -0
- data/lib/pg-verify/model/allocation_set.rb +28 -0
- data/lib/pg-verify/model/assignment.rb +34 -0
- data/lib/pg-verify/model/component.rb +40 -0
- data/lib/pg-verify/model/dcca/hazard.rb +16 -0
- data/lib/pg-verify/model/dcca.rb +67 -0
- data/lib/pg-verify/model/expression.rb +106 -0
- data/lib/pg-verify/model/graph.rb +58 -0
- data/lib/pg-verify/model/model.rb +10 -0
- data/lib/pg-verify/model/parsed_expression.rb +77 -0
- data/lib/pg-verify/model/simulation/trace.rb +43 -0
- data/lib/pg-verify/model/simulation/variable_state.rb +23 -0
- data/lib/pg-verify/model/source_location.rb +45 -0
- data/lib/pg-verify/model/specs/spec.rb +44 -0
- data/lib/pg-verify/model/specs/spec_result.rb +25 -0
- data/lib/pg-verify/model/specs/spec_set.rb +43 -0
- data/lib/pg-verify/model/specs/specification.rb +50 -0
- data/lib/pg-verify/model/transition.rb +41 -0
- data/lib/pg-verify/model/validation/assignment_to_state_variable_validation.rb +26 -0
- data/lib/pg-verify/model/validation/empty_state_set_validation.rb +18 -0
- data/lib/pg-verify/model/validation/errors.rb +119 -0
- data/lib/pg-verify/model/validation/foreign_assignment_validation.rb +30 -0
- data/lib/pg-verify/model/validation/unknown_token_validation.rb +35 -0
- data/lib/pg-verify/model/validation/validation.rb +23 -0
- data/lib/pg-verify/model/variable.rb +47 -0
- data/lib/pg-verify/model/variable_set.rb +84 -0
- data/lib/pg-verify/nusmv/nusmv.rb +23 -0
- data/lib/pg-verify/nusmv/runner.rb +124 -0
- data/lib/pg-verify/puml/puml.rb +23 -0
- data/lib/pg-verify/shell/loading/line_animation.rb +36 -0
- data/lib/pg-verify/shell/loading/loading_animation.rb +80 -0
- data/lib/pg-verify/shell/loading/loading_prompt.rb +43 -0
- data/lib/pg-verify/shell/loading/no_animation.rb +20 -0
- data/lib/pg-verify/shell/shell.rb +30 -0
- data/lib/pg-verify/simulation/simulation.rb +7 -0
- data/lib/pg-verify/simulation/simulator.rb +90 -0
- data/lib/pg-verify/simulation/state.rb +53 -0
- data/lib/pg-verify/transform/hash_transformation.rb +104 -0
- data/lib/pg-verify/transform/nusmv_transformation.rb +261 -0
- data/lib/pg-verify/transform/puml_transformation.rb +89 -0
- data/lib/pg-verify/transform/transform.rb +8 -0
- data/lib/pg-verify/version.rb +5 -0
- data/lib/pg-verify.rb +47 -0
- data/pg-verify.gemspec +38 -0
- data/sig/pg-verify.rbs +4 -0
- metadata +226 -0
@@ -0,0 +1,61 @@
|
|
1
|
+
####################################################################
|
2
|
+
# Model definition
|
3
|
+
####################################################################
|
4
|
+
|
5
|
+
model :RailroadCrossing do
|
6
|
+
|
7
|
+
####################################################################
|
8
|
+
# Train Components
|
9
|
+
####################################################################
|
10
|
+
|
11
|
+
# Train environment representing the actual physical train
|
12
|
+
graph :EnvTrain do
|
13
|
+
# velocity & position
|
14
|
+
end
|
15
|
+
|
16
|
+
# Position sensor of the train
|
17
|
+
graph :SensorTrainPosition do
|
18
|
+
states :idle, :send_close_msg, :send_is_closed_msg
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
graph :SensorTrainRadioModule do
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
# Actuator to
|
27
|
+
graph :ActTrainBrake do
|
28
|
+
states :idle, :active
|
29
|
+
end
|
30
|
+
|
31
|
+
graph :CtrlTrain do
|
32
|
+
end
|
33
|
+
|
34
|
+
####################################################################
|
35
|
+
# Crossing Components
|
36
|
+
####################################################################
|
37
|
+
|
38
|
+
graph :ActBarrierMotor do
|
39
|
+
var motor_speed: [-1, 0, 1], init: 0
|
40
|
+
|
41
|
+
states :idle, :opening, :closing
|
42
|
+
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
graph :CtrlCrossing do
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
|
54
|
+
|
55
|
+
|
56
|
+
graph :WingControl do
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
|
2
|
+
MAX_POS = 20
|
3
|
+
TREE_POS = 13
|
4
|
+
|
5
|
+
transient error :BreakFailure
|
6
|
+
transient error :SensorFailure
|
7
|
+
|
8
|
+
component :Train do
|
9
|
+
states :driving
|
10
|
+
|
11
|
+
var :position => (0..MAX_POS), init: 0
|
12
|
+
|
13
|
+
transition :driving => :driving do
|
14
|
+
guard "Breaks == idle"
|
15
|
+
action "position := position + 1"
|
16
|
+
precon "position < #{MAX_POS}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
component :TreeSensor do
|
21
|
+
states :idle, :signal
|
22
|
+
transition :idle => :signal do
|
23
|
+
guard "position >= #{TREE_POS - 3} && SensorFailure == no"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
component :Breaks do
|
28
|
+
states :idle, :active
|
29
|
+
transition :idle => :active do
|
30
|
+
guard "TreeSensor == signal && BreakFailure == no"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
specify "The train" do
|
35
|
+
it "reaches the tree" => :"F position == #{TREE_POS - 1}"
|
36
|
+
|
37
|
+
assuming "there are no errors" => :"G BreakFailure == no && G SensorFailure == no" do
|
38
|
+
it "stops before the tree" => :"G position < #{TREE_POS}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
hazard "The train runs into a tree" => :"position == #{TREE_POS}"
|
43
|
+
hazard "The train does not reach its destination" => :"F position == #{MAX_POS}"
|
@@ -0,0 +1,99 @@
|
|
1
|
+
####################################################################
|
2
|
+
# Model definition
|
3
|
+
####################################################################
|
4
|
+
|
5
|
+
# Define a component called 'Hand'.
|
6
|
+
component :Hand do
|
7
|
+
# The hand can be touching the fence or be somewhere else
|
8
|
+
# It starts in 'somewhere' as that state is listed first
|
9
|
+
states :somewhere, :at_fence
|
10
|
+
|
11
|
+
# Transition non-deterministically between those states
|
12
|
+
transition :somewhere => :somewhere
|
13
|
+
transition :somewhere => :at_fence
|
14
|
+
transition :at_fence => :somewhere
|
15
|
+
transition :at_fence => :at_fence
|
16
|
+
end
|
17
|
+
|
18
|
+
# Define another component called 'PowerSwitch', which also
|
19
|
+
# transitions non-deterministically.
|
20
|
+
component :PowerSwitch do
|
21
|
+
states :off, :on
|
22
|
+
transition :off => :off
|
23
|
+
transition :off => :on
|
24
|
+
transition :on => :off
|
25
|
+
transition :on => :on
|
26
|
+
end
|
27
|
+
|
28
|
+
component :Fence do
|
29
|
+
# The fence has no states we are interested in
|
30
|
+
states :exists
|
31
|
+
|
32
|
+
# The fence has a voltage which can go up to 15
|
33
|
+
var :voltage => (0..15), init: 0
|
34
|
+
|
35
|
+
# The voltage increases when the power switch is on
|
36
|
+
transition :exists => :exists do
|
37
|
+
guard "PowerSwitch == on"
|
38
|
+
action "voltage := voltage + 1"
|
39
|
+
end
|
40
|
+
# ..and instantly stops when the switch is off
|
41
|
+
transition :exists => :exists do
|
42
|
+
guard "PowerSwitch == off"
|
43
|
+
action "voltage := 0"
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
component :Pain do
|
49
|
+
# We can either be in pain or not
|
50
|
+
states :No, :Yes
|
51
|
+
# Using regular variables with string interpolation
|
52
|
+
pain_threshold = 7
|
53
|
+
transition :No => :Yes do
|
54
|
+
guard "Hand == at_fence && voltage >= #{pain_threshold}"
|
55
|
+
end
|
56
|
+
transition :Yes => :No do
|
57
|
+
guard "Hand == somewhere || voltage < #{pain_threshold}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
####################################################################
|
62
|
+
# Validity tests
|
63
|
+
####################################################################
|
64
|
+
|
65
|
+
# Specification of validity characteristics regarding the hand.
|
66
|
+
# The 'specify' block serves as a namespace/container for the contained specifications
|
67
|
+
specify "The Hand" do
|
68
|
+
# Define some simple specs using a description text and an LTL expression
|
69
|
+
it "isn't always touching the fence" => :"F Hand == somewhere"
|
70
|
+
it "isn't always away form the fence" => :"F Hand == at_fence"
|
71
|
+
end
|
72
|
+
|
73
|
+
# Specification of validity characteristics regarding the pain
|
74
|
+
specify "The pain" do
|
75
|
+
# Use a regular LTL Formula as the expression
|
76
|
+
it "is felt at some point" => :"F Pain == yes"
|
77
|
+
# Use a more declarative syntax. This becomes useful for complex expressions
|
78
|
+
# as LTL patterns can be used very easily
|
79
|
+
it "is always felt at some point" => ltl.globally.exists(:"Pain == yes")
|
80
|
+
|
81
|
+
# Pattern: 'Universality', range: 'after q'
|
82
|
+
it "is felt after the switch is activated" => ltl.after(:"PowerSwitch == on").exists(:"Pain == yes")
|
83
|
+
# Pattern: 'Absence', range: 'before q'
|
84
|
+
it "is never felt before the switch is activated" => ltl.before(:"PowerSwitch == on").never(:"Pain == yes")
|
85
|
+
# Pattern: 'Reaction', range: 'global'
|
86
|
+
it "always reacts to the switch being activated" => ltl.globally.reacts(:"PowerSwitch == on", :"Pain == yes")
|
87
|
+
|
88
|
+
# Define an assumption. That assumption must be true for all contained specs
|
89
|
+
assuming "the switch is never activated" => :"G PowerSwitch == off" do
|
90
|
+
it "is never felt " => :"G Pain == no"
|
91
|
+
end
|
92
|
+
|
93
|
+
# Assumptions can be nested and used with the declarative syntax.
|
94
|
+
assuming "the switch is activated" => ltl.globally.exists(:"PowerSwitch == on") do
|
95
|
+
assuming "the hand never touches the fence" => :"G Hand != at_fence" do
|
96
|
+
it "is never felt " => :"G Pain == no"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
---- Ruby/Json style ----
|
2
|
+
|
3
|
+
model FarmFence {
|
4
|
+
|
5
|
+
graph Fence (exists) {
|
6
|
+
var voltage (0..15) : 0
|
7
|
+
|
8
|
+
transition exists => exists {
|
9
|
+
PowerSwitch == on / voltage := voltage + 1
|
10
|
+
}
|
11
|
+
transition exists => exists {
|
12
|
+
PowerSwitch == off / voltage := voltage + 1
|
13
|
+
}
|
14
|
+
}
|
15
|
+
|
16
|
+
}
|
17
|
+
|
18
|
+
---- Yaml/Python style ----
|
19
|
+
|
20
|
+
model FarmFence:
|
21
|
+
|
22
|
+
graph Fence { exists }:
|
23
|
+
voltage (0..15) init 0
|
24
|
+
|
25
|
+
transition exists => exists:
|
26
|
+
PowerSwitch == on / voltage := voltage + 1
|
27
|
+
|
28
|
+
transition exists => exists:
|
29
|
+
PowerSwitch == off / voltage := 0
|
Binary file
|
Binary file
|
@@ -0,0 +1,359 @@
|
|
1
|
+
# pg-verify
|
2
|
+
|
3
|
+
pg-verify ist ein textbasiertes CLI-Programm zur Entwicklung und Einbindung von
|
4
|
+
Programmgraphen im Kontext der formalen Sicherheitsanalyse.
|
5
|
+
|
6
|
+
Ziel ist es, ein anwenderfreundliches, robustes und zukunftssicheres System zu entwickeln,
|
7
|
+
das möglichst im Hintergrund bleibt und Platz für das Wesentliche lässt: Das Modellieren.
|
8
|
+
|
9
|
+
## Beispielprojekt
|
10
|
+
|
11
|
+
Damit klarer wird, welches System wir uns vorstellen, demonstrieren wir im Folgenden ein kleines
|
12
|
+
Beispielprojekt. Es handelt sich um eine leicht modifizierte Version
|
13
|
+
des "Weidezaun"-Projektes aus der Vorlesung.
|
14
|
+
Die Ausgaben sind teilweise noch von Hand erstellt, da die Implementierung noch
|
15
|
+
nicht weit genug fortgeschritten ist.
|
16
|
+
Die beschriebene Funktionalität dient natürlich erstmal als Gesprächsgrundlage.
|
17
|
+
|
18
|
+
### Die Entwicklungsumgebung
|
19
|
+
|
20
|
+
Da wir vorhaben, ein CLI-Programm zu erstellen, das mit "Plain Text" arbeitet,
|
21
|
+
ist jede Entwicklungsumgebung denkbar.
|
22
|
+
Das folgende Bild zeigt also nur ein Beispiel.
|
23
|
+
(Mehr dazu [hier](#warum-cli-und-plain-text))
|
24
|
+
|
25
|
+
![alt text](./definition.png)
|
26
|
+
|
27
|
+
Links im Bild sieht man die Definition eines Programmgraphen über die
|
28
|
+
Ruby DSL. Rechts sieht man die generierte Grafik. Die Grafik wird aktualisiert sobald
|
29
|
+
man `pg-verify show` ausführt, so wie unten abgebildet.
|
30
|
+
|
31
|
+
### Das Weidezaun-Beispiel in der Ruby DSL
|
32
|
+
|
33
|
+
Zur besseren Lesbarkeit folgt das Weidezaun-Beispiel in der Ruby DSL:
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
model :FarmFence do
|
37
|
+
# Define a component called 'Hand'.
|
38
|
+
graph :Hand do
|
39
|
+
# The hand can be touching the fence or be somewhere else
|
40
|
+
# It starts in 'somewhere' as that state is listed first
|
41
|
+
states :somewhere, :at_fence
|
42
|
+
|
43
|
+
# Transition non-deterministically between those states
|
44
|
+
transition :somewhere => :somewhere
|
45
|
+
transition :somewhere => :at_fence
|
46
|
+
transition :at_fence => :somewhere
|
47
|
+
transition :at_fence => :at_fence
|
48
|
+
end
|
49
|
+
|
50
|
+
# Define another component called 'PowerSwitch', which also
|
51
|
+
# transitions non-deterministically.
|
52
|
+
graph :PowerSwitch do
|
53
|
+
states :off, :on
|
54
|
+
transition :off => :off
|
55
|
+
transition :off => :on
|
56
|
+
transition :on => :off
|
57
|
+
transition :on => :on
|
58
|
+
end
|
59
|
+
|
60
|
+
graph :Fence do
|
61
|
+
# The fence has no states we are interested in
|
62
|
+
states :exists
|
63
|
+
|
64
|
+
# The fence has a voltage which can go up to 15
|
65
|
+
var :voltage => (0..15), init: 0
|
66
|
+
|
67
|
+
# The voltage increases when the power switch is on
|
68
|
+
transition :exists => :exists do
|
69
|
+
guard "PowerSwitch == on"
|
70
|
+
action "voltage := voltage + 1"
|
71
|
+
end
|
72
|
+
# ..and instantly stops when the switch is off
|
73
|
+
transition :exists => :exists do
|
74
|
+
guard "PowerSwitch == off"
|
75
|
+
action "voltage := 0"
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
graph :Pain do
|
81
|
+
# We can either be in pain or not
|
82
|
+
states :No, :Yes
|
83
|
+
# Using regular variables with string interpolation
|
84
|
+
pain_threshold = 7
|
85
|
+
transition :No => :Yes do
|
86
|
+
guard "Hand == at_fence && voltage >= #{pain_threshold}"
|
87
|
+
end
|
88
|
+
transition :Yes => :No do
|
89
|
+
guard "Hand == somewhere || voltage < #{pain_threshold}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
```
|
94
|
+
|
95
|
+
Und hier die generierte Grafik:
|
96
|
+
|
97
|
+
![alt text](./diagram.png)
|
98
|
+
|
99
|
+
### Validitätsprüfungen
|
100
|
+
|
101
|
+
![alt text](./validity.png)
|
102
|
+
|
103
|
+
Oben im Bild ist die Definition von Spezifikationen zur Validitätsprüfung dargestellt.
|
104
|
+
Führt man `pg-verify test` aus, erhält man die unten dargestellte Ausgabe.
|
105
|
+
(Das Feature ist noch nicht implementiert. Tatsächlich wären hier einige Tests fehlgeschlagen.)
|
106
|
+
Die Syntax ist vom beliebten Test-Framework [Rspec](https://rspec.info/) inspiriert.
|
107
|
+
Im Fehlerfall würde der Ablauf ausgegeben, der die Formel verletzt.
|
108
|
+
|
109
|
+
Nach der Fehlerintegration könnte man Fehler beispielsweise so ausschließen:
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
BEP = 150
|
113
|
+
errors = [ :BreaksError, :SensorError ]
|
114
|
+
# ...
|
115
|
+
no_errors = errors.map { |error| "G #{error} == no" }.join(" && ")
|
116
|
+
specify "The train" do
|
117
|
+
assuming "there are no errors" => no_errors do
|
118
|
+
it "reaches BEP" => :"G Tain.position > #{BEP}"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
```
|
122
|
+
|
123
|
+
Zudem kann man die Ruby DSL so erweitern, dass Fehlerautomaten
|
124
|
+
im internen Datenmodell von gewöhnlichen Komponenten unterschieden werden können.
|
125
|
+
Damit kann man eine knappe Syntax anbieten und die DCCA vollautomatisch durchführen:
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
# Erzeugt Fehlerautomaten für die Bremse & den Zugsensor
|
129
|
+
persistent error :Breaks
|
130
|
+
transient error :Sensor
|
131
|
+
|
132
|
+
# Validität unter Ausschluss von Fehlern
|
133
|
+
# 'no_erros' kann hier automatisch generiert werden
|
134
|
+
specify "The train" do
|
135
|
+
assuming "there are no errors" => no_errors do
|
136
|
+
it "reaches BEP" => :"G Tain.position > #{BEP}"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Definition einer Gefährdung für die DCCA. Ausführbar mit: 'pg-verify dcca'
|
141
|
+
hazard "Train on unsecured railroad crossing" \
|
142
|
+
=> :"Barrier.angle > #{barrier_closed_angle} && Train.position >= #{train_pos_gep} && Train.position <= #{tain_pos_sp}"
|
143
|
+
```
|
144
|
+
|
145
|
+
### Validitätsprüfungen in der Ruby DSL
|
146
|
+
|
147
|
+
Zur besseren Lesbarkeit ist hier nochmal die Spezifikation der
|
148
|
+
Validitätsprüfungen für das Weidezaun-Beispiel in der Ruby DSL:
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
# Specification of validity characteristics regarding the hand.
|
152
|
+
# The 'specify' block serves as a namespace/container for the contained specifications
|
153
|
+
specify "The Hand" do
|
154
|
+
# Define some simple specs using a description text and an LTL expression
|
155
|
+
it "isn't always touching the fence" => :"F Hand == somewhere"
|
156
|
+
it "isn't always away form the fence" => :"F Hand == at_fence"
|
157
|
+
end
|
158
|
+
|
159
|
+
# Specification of validity characteristics regarding the pain
|
160
|
+
specify "The pain" do
|
161
|
+
# Use a regular LTL Formula as the expression
|
162
|
+
it "is felt at some point" => :"F Pain == yes"
|
163
|
+
# Use a more declarative syntax. This becomes useful for complex expressions
|
164
|
+
# as LTL patterns can be used very easily
|
165
|
+
it "is always felt at some point" => ltl.globally.exists(:"Pain == yes")
|
166
|
+
|
167
|
+
# Pattern: 'Universality', range: 'after q'
|
168
|
+
it "is felt after the switch is activated" => ltl.after(:"PowerSwitch == on").exists(:"Pain == yes")
|
169
|
+
# Pattern: 'Absence', range: 'before q'
|
170
|
+
it "is never felt before the switch is activated" => ltl.before(:"PowerSwitch == on").never(:"Pain == yes")
|
171
|
+
# Pattern: 'Reaction', range: 'global'
|
172
|
+
it "always reacts to the switch being activated" => ltl.globally.reacts(:"PowerSwitch == on", :"Pain == yes")
|
173
|
+
|
174
|
+
# Define an assumption. That assumption must be true for all contained specs
|
175
|
+
assuming "the switch is never activated" => :"G PowerSwitch == off" do
|
176
|
+
it "is never felt " => :"G Pain == no"
|
177
|
+
end
|
178
|
+
|
179
|
+
# Assumptions can be nested and used with the declarative syntax.
|
180
|
+
assuming "the switch is activated" => ltl.globally.exists(:"PowerSwitch == on") do
|
181
|
+
assuming "the hand never touches the fence" => :"G Hand != at_fence" do
|
182
|
+
it "is never felt " => :"G Pain == no"
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
```
|
187
|
+
|
188
|
+
## Eingabesprachen
|
189
|
+
|
190
|
+
Die bisherigen Beispiele wurden in der Ruby DSL verfasst.
|
191
|
+
Wir planen aber zusätzlich eine eigene Eingabesprache anzubieten, die mit
|
192
|
+
einem language server integriert ist (mehr dazu [hier](#language-server)).
|
193
|
+
|
194
|
+
Es folgt eine Gegenüberstellung von verschiedenen Möglichkeiten für eine solche
|
195
|
+
Eingabesprache. Das resultierende Modell wäre in allen Fällen identisch.
|
196
|
+
|
197
|
+
In der Ruby DSL:
|
198
|
+
|
199
|
+
```ruby
|
200
|
+
model :FarmFence do
|
201
|
+
|
202
|
+
graph :Fence do
|
203
|
+
states :exists
|
204
|
+
|
205
|
+
var voltage: (0..15), init "voltage >= 1 && voltage <= 5"
|
206
|
+
|
207
|
+
transition :exists => :exists {
|
208
|
+
guard "PowerSwitch == on"
|
209
|
+
action "voltage := voltage + 1"
|
210
|
+
}
|
211
|
+
transition :exists => :exists {
|
212
|
+
guard "PowerSwitch == on"
|
213
|
+
action "voltage := voltage + 1"
|
214
|
+
}
|
215
|
+
end
|
216
|
+
|
217
|
+
end
|
218
|
+
```
|
219
|
+
|
220
|
+
Von Ruby und Json inspirierte Möglichkeit für eine Eingabesprache:
|
221
|
+
|
222
|
+
```
|
223
|
+
model FarmFence {
|
224
|
+
|
225
|
+
graph Fence (exists) {
|
226
|
+
var voltage (0..15) : voltage >= 1 && voltage <= 5
|
227
|
+
|
228
|
+
transition exists => exists {
|
229
|
+
PowerSwitch == on / voltage := voltage + 1
|
230
|
+
}
|
231
|
+
transition exists => exists {
|
232
|
+
PowerSwitch == off / voltage := voltage + 1
|
233
|
+
}
|
234
|
+
}
|
235
|
+
}
|
236
|
+
```
|
237
|
+
|
238
|
+
Von Python und Yaml inspirierte Möglichkeit für eine Eingabesprache:
|
239
|
+
|
240
|
+
```
|
241
|
+
model FarmFence:
|
242
|
+
|
243
|
+
graph Fence { exists }:
|
244
|
+
var voltage (0..15) init voltage >= 1 && voltage <= 5
|
245
|
+
|
246
|
+
transition exists => exists:
|
247
|
+
PowerSwitch == on / voltage := voltage + 1
|
248
|
+
|
249
|
+
transition exists => exists:
|
250
|
+
PowerSwitch == off / voltage := 0
|
251
|
+
|
252
|
+
```
|
253
|
+
|
254
|
+
|
255
|
+
## Language Server
|
256
|
+
|
257
|
+
Language Servers ermöglichen es, effizient Sprach-Features zu implementieren. Dazu gehören zum Beispiel:
|
258
|
+
- Code-Vervollständigung
|
259
|
+
- Fehler-Überprüfung und -diagnose
|
260
|
+
- Springen zur Definition einer Funktion
|
261
|
+
|
262
|
+
Wir möchten versuchen, pg-verify in einen Language Server zu integrieren. Allerdings wissen wir mangels eigener Erfahrung nicht, ob das mit angemessenem Aufwand möglich ist und können deshalb nicht versprechen, dass dieses Fearture am Ende im Produkt enthalten sein wird.
|
263
|
+
|
264
|
+
Durch diese Integration möchten wir den Entwickler:innen ermöglichen:
|
265
|
+
- Sich auf die Modellierung der Graphen zu konzentrieren, anstatt wieder und wieder die gleiche Variable auszuschreiben
|
266
|
+
- Sofort zu sehen, was falsch ist, anstatt stundenlang selbst nach Fehlern zu suchen
|
267
|
+
- Somit möglichst produktiv zu arbeiten
|
268
|
+
|
269
|
+
Weitere große Vorteile der Verwendung eines Language Servers sind Wiederverwendbarkeit und Plattformunabhängigkeit.
|
270
|
+
Wiederverwendbarkeit wird dadurch erreicht, dass man die Sprach-Features nur einmal definieren muss
|
271
|
+
und diese dann über wohldefinierte Schnittstellen vom Client aus aufrufen kann.
|
272
|
+
Plattformunabhängigkeit wird dadurch erreicht, dass man Language Clients in jedem erdenklichen Text-Editor bzw. IDE implementieren kann.
|
273
|
+
Beispielhaft soll ein Language Client für Visual Studio Code implementiert werden.
|
274
|
+
Dieser Editor ist für alle großen Betriebssysteme - Linux, Mac und Windows verfügbar.
|
275
|
+
Durch die angesprochene Plattformunabhängigkeit wollen wir einen Mehrwert gegenüber dem
|
276
|
+
aktuell verwendeten Produkt schaffen, welches nur auf Windows über MS VisualStudio verwendet werden kann.
|
277
|
+
|
278
|
+
Zudem ist es wahrscheinlich möglich, die oben angesprochenen Validitätsüberprüfungen direkt
|
279
|
+
über den Language Server abzufragen und in Echtzeit zur Verfügung zu stellen.
|
280
|
+
|
281
|
+
# Features
|
282
|
+
|
283
|
+
Im Folgenden haben wir einige Ideen für Features aufgelistet:
|
284
|
+
|
285
|
+
- Einlesen von Modellen über Ruby DSL, JSON und YAML
|
286
|
+
- Einlesen einer eigenen Eingabesprache mit Unterschützung durch einen Language Server
|
287
|
+
- Ausgeben von Modellen in JSON, YAML, PlantUML
|
288
|
+
- Integration von NuSMV, Prism und ggf. weiterer Model Checker.
|
289
|
+
- Simulation von Modellen und Ausgabe als Video oder GIF
|
290
|
+
- Integriertes Test-Framework zur Validitätsprüfung
|
291
|
+
- Deklarative Syntax zur Verwendung der "LTL-Pattern" aus der Vorlesung
|
292
|
+
- Automatische DCCA
|
293
|
+
- Installation über ein Kommando (`gem install pg-verify`)
|
294
|
+
- Einfache Einarbeitung (mit Kommando `pg-verify init`)
|
295
|
+
- Hier wird ein Beispielprojekt angelegt um die Projektstruktur vorzugeben
|
296
|
+
- Ansprechende Dokumentation
|
297
|
+
- Konfigurationsmöglichkeiten
|
298
|
+
- Hilfreiche Fehlermeldungen
|
299
|
+
- Unterstützung eines Language Servers
|
300
|
+
- Implementierung eines Language Clients für Visual Studio Code
|
301
|
+
|
302
|
+
# Warum CLI und Plain Text
|
303
|
+
|
304
|
+
Das Verständnis eines Systems ist zwar die Grundlage der Modellierung, aber nur ihr Anfang.
|
305
|
+
Erst durch die Konzeptualisierung ist es möglich, Modelle festzuhalten, zu kommunizieren,
|
306
|
+
Denkfehler aufzudecken und sie maschinell zu verarbeiten.
|
307
|
+
|
308
|
+
Mit den folgenden Argumenten wollen wir darstellen, warum die Verwendung von "Plain Text" und eines CLI-Programms
|
309
|
+
für die Konzeptualisierung bzw. Festschreibung von Modellen besser geeignet ist als eine graphische Lösung.
|
310
|
+
(Mit "Pain Text" meinen wir den Ansatz, dass alle Projektdateien ausschließlich Text enthaten,
|
311
|
+
der schon für sich und ohne die Verwendung von Tools verständlich ist)
|
312
|
+
|
313
|
+
## Unkomplizierte Eingabe
|
314
|
+
|
315
|
+
Graphische Darstellungen sind sehr nützlich, um Modelle zu verstehen.
|
316
|
+
Dabei liegt ihr Vorteil aber in der effizienten *Aufnahme* von Informationen,
|
317
|
+
durch den Betrachter.
|
318
|
+
|
319
|
+
Bei der *Ausgabe* der eignenen Gedanken können graphische Editoren oft hinderlich sein.
|
320
|
+
Nebensächlichkeiten, wie das Layout und eine umständliche Navigation im UI
|
321
|
+
stehen dem eigentlichen Ziel - der Modellierung - eher im Weg.
|
322
|
+
|
323
|
+
Wir wollen die Verständlichkeit einer graphischen Ausgabe
|
324
|
+
mit der unkomplizierten Eingabe über Text verbinden.
|
325
|
+
|
326
|
+
## Einfache Zusammenarbeit
|
327
|
+
|
328
|
+
Tools wie Git(-Hub) sind auf die Verarbeitung von Text ausgelegt.
|
329
|
+
Versionskontrolle, Code Reviews und das Beheben von Merge-Konflikten
|
330
|
+
funktionieren deutlich besser mit "Plain Text".
|
331
|
+
|
332
|
+
## Zeitlosigkeit & Technologie-Unabhängigkeit
|
333
|
+
|
334
|
+
Die Wartung und Instandhaltung von UI-basierten Programmen ist nur mit großem Aufwand möglich.
|
335
|
+
Bibliotheken veralten und Design Trends ändern sich. Während Programme wie Eclipse schlecht gealtert sind,
|
336
|
+
erfreuen sich Command Line Tools wie "git" oder "make" nach wie vor großer Beliebtheit.
|
337
|
+
|
338
|
+
Trotz des Aufkommens neuer Technologien stellen CLI-Programme und und die Verwendung von
|
339
|
+
Plain Text eine Konstante dar.
|
340
|
+
|
341
|
+
## Einfache Installation
|
342
|
+
|
343
|
+
Die Leichtgewichtigkeit eines CLI-Programms vereinfacht die Installation und lässt weniger
|
344
|
+
Spielraum für Fehler. Durch minimale Abhängigkeiten kann die Funktionalität
|
345
|
+
auf verschiednen Systemen sichergestellt werden.
|
346
|
+
|
347
|
+
## Automatisierung und Flexibilität
|
348
|
+
|
349
|
+
CLI-Programme können einfach in Arbeitsabläufe eingebunden werden.
|
350
|
+
Beispielsweise wäre es möglich, Tests für Modelle zu schreiben, die mittels "continuous integration"
|
351
|
+
für jeden Pull Request auf GitHub ausgeführt werden.
|
352
|
+
(Das `pg-verify init` Kommando könnte eine solche CI Pipeline automatisch definieren)
|
353
|
+
|
354
|
+
## Zielgruppe
|
355
|
+
|
356
|
+
Da die Zielgruppe unseres Projektes Informatikstudierende sind,
|
357
|
+
kann man grundlegende Kenntnisse im Umgang mit CLI-Programmen erwarten.
|
358
|
+
Die einheitliche Meinung von Kommiliton:innen aus unserem Jahrgang ist es,
|
359
|
+
dass die beschriebene Anwednung die Arbeit am Projekt erleichtert hätte.
|
Binary file
|
data/exe/pg-verify
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
model :TestAction do
|
2
|
+
graph :Test do
|
3
|
+
var position: (0..100), init: 0
|
4
|
+
states :go
|
5
|
+
transition :go => :go do
|
6
|
+
guard "position < 100"
|
7
|
+
action "position := position + 1"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
specify "The position" do
|
11
|
+
it "does not start at the limit" => :"! G position == 100"
|
12
|
+
it "reaches the limit" => :"F position == 100"
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
model :TestAction do
|
2
|
+
graph :Test do
|
3
|
+
var position: (0..100), init: 0
|
4
|
+
states :go
|
5
|
+
transition :go => :go do
|
6
|
+
guard "position < 10"
|
7
|
+
action "position := position + 1"
|
8
|
+
end
|
9
|
+
transition :go => :go do
|
10
|
+
guard "position == 10"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
specify "The position" do
|
14
|
+
it "does not start at the limit" => :"! G position == 100"
|
15
|
+
it "does not reaches the limit" => :"! F position == 100"
|
16
|
+
it "does stay at the threshold" => :"F G position == 10"
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
model :TestAction do
|
2
|
+
graph :Test do
|
3
|
+
var position: (0..10), init: 0
|
4
|
+
states :go
|
5
|
+
transition :go => :go do
|
6
|
+
guard "position < 10"
|
7
|
+
action "position := position + 1"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
specify "The position" do
|
11
|
+
it "starts at 0" => :"position == 0"
|
12
|
+
it "goes to 1" => :"X position == 1"
|
13
|
+
it "goes to 2" => :"X X position == 2"
|
14
|
+
it "reaches 10" => :"F position == 10"
|
15
|
+
end
|
16
|
+
end
|