pg-verify 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/Gemfile +10 -0
  4. data/Gemfile.lock +98 -0
  5. data/README.md +29 -0
  6. data/Rakefile +62 -0
  7. data/bin/console +15 -0
  8. data/bin/pg-verify.rb +18 -0
  9. data/bin/setup +8 -0
  10. data/calc.ebnf +21 -0
  11. data/data/config/pg-verify.yml +66 -0
  12. data/data/nusmv.sample.smv +179 -0
  13. data/data/project-template/.gitignore.resource +4 -0
  14. data/data/project-template/.pg-verify.yml +0 -0
  15. data/data/project-template/README.md +18 -0
  16. data/data/project-template/addon/.keep +0 -0
  17. data/data/project-template/program-graph.rb.resource +103 -0
  18. data/devpg +5 -0
  19. data/doc/examples/railroad_crossing.rb +61 -0
  20. data/doc/examples/train-tree.rb +43 -0
  21. data/doc/examples/weidezaun.rb +99 -0
  22. data/doc/examples/weidezaun.txt +29 -0
  23. data/doc/expose/definition.png +0 -0
  24. data/doc/expose/diagram.png +0 -0
  25. data/doc/expose/expose.md +359 -0
  26. data/doc/expose/validity.png +0 -0
  27. data/exe/pg-verify +4 -0
  28. data/integration_tests/ruby_dsl/001_states.rb +10 -0
  29. data/integration_tests/ruby_dsl/002_transitions.rb +10 -0
  30. data/integration_tests/ruby_dsl/003_actions.rb +14 -0
  31. data/integration_tests/ruby_dsl/004_guards.rb +18 -0
  32. data/integration_tests/ruby_dsl/005_variables.rb +16 -0
  33. data/integration_tests/ruby_dsl/006_state_variables.rb +26 -0
  34. data/integration_tests/ruby_dsl/007_variable_initialization.rb +28 -0
  35. data/integration_tests/ruby_dsl/008_state_initialization.rb +19 -0
  36. data/integration_tests/ruby_dsl/009_shared_variables.rb +26 -0
  37. data/integration_tests/ruby_dsl/010_complex_guards.rb +18 -0
  38. data/integration_tests/ruby_dsl/011_complex_actions.rb +16 -0
  39. data/integration_tests/ruby_dsl/012_error_components.rb +9 -0
  40. data/integration_tests/ruby_dsl/013_hazards.rb +25 -0
  41. data/integration_tests/ruby_dsl/014_tau_transitions.rb +26 -0
  42. data/integration_tests/ruby_dsl/015_basic_dcca.rb +19 -0
  43. data/integration_tests/ruby_dsl/016_pressure_tank.rb +146 -0
  44. data/lib/pg-verify/cli/cli.rb +235 -0
  45. data/lib/pg-verify/core/cmd_runner.rb +151 -0
  46. data/lib/pg-verify/core/core.rb +38 -0
  47. data/lib/pg-verify/core/extensions/array_extensions.rb +11 -0
  48. data/lib/pg-verify/core/extensions/enumerable_extensions.rb +19 -0
  49. data/lib/pg-verify/core/extensions/nil_extensions.rb +7 -0
  50. data/lib/pg-verify/core/extensions/string_extensions.rb +84 -0
  51. data/lib/pg-verify/core/shell/colorizer.rb +136 -0
  52. data/lib/pg-verify/core/shell/shell.rb +0 -0
  53. data/lib/pg-verify/core/util.rb +146 -0
  54. data/lib/pg-verify/doctor/doctor.rb +180 -0
  55. data/lib/pg-verify/ebnf_parser/ast.rb +31 -0
  56. data/lib/pg-verify/ebnf_parser/ebnf_parser.rb +26 -0
  57. data/lib/pg-verify/ebnf_parser/expression_parser.rb +177 -0
  58. data/lib/pg-verify/ebnf_parser/expression_parser2.rb +422 -0
  59. data/lib/pg-verify/ebnf_parser/expressions.ebnf +33 -0
  60. data/lib/pg-verify/ebnf_parser/expressions.peg +52 -0
  61. data/lib/pg-verify/ebnf_parser/parser_result.rb +26 -0
  62. data/lib/pg-verify/interpret/component_context.rb +125 -0
  63. data/lib/pg-verify/interpret/graph_context.rb +85 -0
  64. data/lib/pg-verify/interpret/interpret.rb +142 -0
  65. data/lib/pg-verify/interpret/pg_script.rb +72 -0
  66. data/lib/pg-verify/interpret/spec/ltl_builder.rb +90 -0
  67. data/lib/pg-verify/interpret/spec/spec_context.rb +32 -0
  68. data/lib/pg-verify/interpret/spec/spec_set_context.rb +67 -0
  69. data/lib/pg-verify/interpret/transition_context.rb +55 -0
  70. data/lib/pg-verify/model/allocation_set.rb +28 -0
  71. data/lib/pg-verify/model/assignment.rb +34 -0
  72. data/lib/pg-verify/model/component.rb +40 -0
  73. data/lib/pg-verify/model/dcca/hazard.rb +16 -0
  74. data/lib/pg-verify/model/dcca.rb +67 -0
  75. data/lib/pg-verify/model/expression.rb +106 -0
  76. data/lib/pg-verify/model/graph.rb +58 -0
  77. data/lib/pg-verify/model/model.rb +10 -0
  78. data/lib/pg-verify/model/parsed_expression.rb +77 -0
  79. data/lib/pg-verify/model/simulation/trace.rb +43 -0
  80. data/lib/pg-verify/model/simulation/variable_state.rb +23 -0
  81. data/lib/pg-verify/model/source_location.rb +45 -0
  82. data/lib/pg-verify/model/specs/spec.rb +44 -0
  83. data/lib/pg-verify/model/specs/spec_result.rb +25 -0
  84. data/lib/pg-verify/model/specs/spec_set.rb +43 -0
  85. data/lib/pg-verify/model/specs/specification.rb +50 -0
  86. data/lib/pg-verify/model/transition.rb +41 -0
  87. data/lib/pg-verify/model/validation/assignment_to_state_variable_validation.rb +26 -0
  88. data/lib/pg-verify/model/validation/empty_state_set_validation.rb +18 -0
  89. data/lib/pg-verify/model/validation/errors.rb +119 -0
  90. data/lib/pg-verify/model/validation/foreign_assignment_validation.rb +30 -0
  91. data/lib/pg-verify/model/validation/unknown_token_validation.rb +35 -0
  92. data/lib/pg-verify/model/validation/validation.rb +23 -0
  93. data/lib/pg-verify/model/variable.rb +47 -0
  94. data/lib/pg-verify/model/variable_set.rb +84 -0
  95. data/lib/pg-verify/nusmv/nusmv.rb +23 -0
  96. data/lib/pg-verify/nusmv/runner.rb +124 -0
  97. data/lib/pg-verify/puml/puml.rb +23 -0
  98. data/lib/pg-verify/shell/loading/line_animation.rb +36 -0
  99. data/lib/pg-verify/shell/loading/loading_animation.rb +80 -0
  100. data/lib/pg-verify/shell/loading/loading_prompt.rb +43 -0
  101. data/lib/pg-verify/shell/loading/no_animation.rb +20 -0
  102. data/lib/pg-verify/shell/shell.rb +30 -0
  103. data/lib/pg-verify/simulation/simulation.rb +7 -0
  104. data/lib/pg-verify/simulation/simulator.rb +90 -0
  105. data/lib/pg-verify/simulation/state.rb +53 -0
  106. data/lib/pg-verify/transform/hash_transformation.rb +104 -0
  107. data/lib/pg-verify/transform/nusmv_transformation.rb +261 -0
  108. data/lib/pg-verify/transform/puml_transformation.rb +89 -0
  109. data/lib/pg-verify/transform/transform.rb +8 -0
  110. data/lib/pg-verify/version.rb +5 -0
  111. data/lib/pg-verify.rb +47 -0
  112. data/pg-verify.gemspec +38 -0
  113. data/sig/pg-verify.rbs +4 -0
  114. 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,4 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative '../bin/pg-verify.rb'
3
+
4
+ PgVerify::PgVerifyCLI.new.run()
@@ -0,0 +1,10 @@
1
+ model :TestTransitions do
2
+ graph :Test do
3
+ states :initial
4
+ end
5
+ specify "The state" do
6
+ it "starts in initial" => :"Test == initial"
7
+ it "stays in initial" => :"G Test == initial"
8
+ it "never leaves initial" => :"! F Test != initial"
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ model :TestTransitions do
2
+ graph :Test do
3
+ states :one, :two, init: :one
4
+ transition :one => :two
5
+ end
6
+ specify "The state" do
7
+ it "starts in one" => :"Test == one"
8
+ it "transitions to two" => :"X Test == two"
9
+ end
10
+ end
@@ -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