pg-verify 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,26 @@
1
+ model :TestAction do
2
+ graph :Engine do
3
+ states :running, :stopped
4
+ end
5
+ graph :Car do
6
+ states :accelerating, :decelerating
7
+
8
+ transition :accelerating => :decelerating do
9
+ guard "Engine == stopped"
10
+ end
11
+ transition :decelerating => :decelerating do
12
+ guard "Engine == stopped"
13
+ end
14
+ transition :accelerating => :accelerating do
15
+ guard "Engine == running"
16
+ end
17
+ transition :decelerating => :accelerating do
18
+ guard "Engine == running"
19
+ end
20
+
21
+ end
22
+ specify "The car" do
23
+ it "accelerates when the engine is running" => :"G (Engine == running => X Car == accelerating)"
24
+ it "decelerates when the engine isn't running" => :"G (Engine != running => X Car == decelerating)"
25
+ end
26
+ end
@@ -0,0 +1,28 @@
1
+ model :TestAction do
2
+ graph :Test do
3
+ state :idle
4
+ var a: (0..1), init: 0
5
+ var b: (0..1), init: "b == 0"
6
+ var c: (-1..2), init: "c > 0 && c < 2"
7
+ var d: (0..10), init: "d == c + c + c"
8
+
9
+ var u: (0..10)
10
+ init "u == 2"
11
+
12
+ var x: (0..10), init: "x >= 5 && x <= 7"
13
+ var y: (0..10), init: "y = x + 1"
14
+ var z: (0..1)
15
+ end
16
+ specify "The variable" do
17
+ it "a is initialized to 0" => :"a == 0"
18
+ it "b is initialized to 0" => :"b == 0"
19
+ it "c is initialized to 1" => :"c == 1"
20
+ it "d is initialized to 3" => :"d == 3"
21
+
22
+ it "u is initialized to 2" => :"u == 2"
23
+
24
+ it "x is initialized to either 5, 6 or 7" => :"x == 5 || x == 6 || x == 7"
25
+ it "y is initialized to either 6, 7 or 8" => :"y == 6 || y == 7 || y == 8"
26
+ it "z is either 0 or 1" => :"z == 0 || z == 1"
27
+ end
28
+ end
@@ -0,0 +1,19 @@
1
+ model :TestAction do
2
+
3
+ graph :GraphZero do
4
+ states :a, :b, :c
5
+ end
6
+ graph :GraphOne do
7
+ states :a, :b, :c
8
+ init "GraphOne == a || GraphOne == b"
9
+ end
10
+ graph :GraphTwo do
11
+ states :a, :b, :c, init: :a
12
+ end
13
+
14
+ specify "The graph" do
15
+ it "GraphZero starts in a or b or c" => :"GraphOne == a || GraphOne == b || GraphOne == c"
16
+ it "GraphOne starts in a or b" => :"GraphOne == a || GraphOne == b"
17
+ it "GraphTwo starts in a" => :"GraphTwo == a"
18
+ end
19
+ end
@@ -0,0 +1,26 @@
1
+ model :TestAction do
2
+
3
+ graph :Car do
4
+ var distance_to_wall: (0..10), init: 0
5
+
6
+ states :DrivingRelentlessly
7
+
8
+ transition :DrivingRelentlessly => :DrivingRelentlessly do
9
+ precon "distance_to_wall > 0"
10
+ action "distance_to_wall := distance_to_wall - 1"
11
+ end
12
+ end
13
+ graph :CrashDetector do
14
+ states :no_crash, :crash, init: :no_crash
15
+
16
+ transition :no_crash => :crash do
17
+ guard "distance_to_wall == 0"
18
+ end
19
+
20
+ end
21
+
22
+ specify "The car" do
23
+ it "starts out driving" => :"CrashDetector == no_crash && Car == DrivingRelentlessly"
24
+ it "crashes horribly" => :"F CrashDetector == crash"
25
+ end
26
+ end
@@ -0,0 +1,18 @@
1
+ model :TestAction do
2
+ graph :Test do
3
+ var a: (0..10), init: 0
4
+ var b: (0..10), init: 10
5
+
6
+ states :initial, :second ,init: :initial
7
+ transition :initial => :second do
8
+ guard "b > 5 && a + 1 == 1 && a != b && Other == second"
9
+ end
10
+ end
11
+ graph :Other do
12
+ states :initial, :second, init: :initial
13
+ transition :initial => :second
14
+ end
15
+ specify "The Test" do
16
+ it "does reach the second state" => :"X X Test == second"
17
+ end
18
+ end
@@ -0,0 +1,16 @@
1
+ model :TestAction do
2
+ graph :Test do
3
+ var a: (0..11), init: 0
4
+ var b: (0..100), init: 0
5
+
6
+ states :go
7
+ transition :go => :go do
8
+ guard "a < 11"
9
+ action "a := a + 1 | b := a * a"
10
+ end
11
+ end
12
+ specify "The variable" do
13
+ it "a reaches 10" => :"F a == 10"
14
+ it "b reaches 100" => :"F b == 100"
15
+ end
16
+ end
@@ -0,0 +1,9 @@
1
+ model :Test do
2
+
3
+ transient error :TransientError
4
+ persistent error :PersistentError
5
+
6
+ specify "The persistent error" do
7
+ it "Stays persistent" => :"G ( PersistentError == Yes => ( ! F PersistentError == No ) )"
8
+ end
9
+ end
@@ -0,0 +1,25 @@
1
+ model :Test do
2
+
3
+ persistent error :BreakFailure
4
+
5
+ graph :Car do
6
+ states :driving
7
+ var distance_to_wall: (0..10), init: 9
8
+
9
+ transition :driving => :driving do
10
+ precon "distance_to_wall > 0"
11
+ guard "distance_to_wall > 5 || BreakFailure == Yes"
12
+ action "distance_to_wall := distance_to_wall - 1"
13
+ end
14
+
15
+ end
16
+
17
+ # Expected Cut Sets: { BreakFailure }
18
+ hazard "The car crashes" => :"distance_to_wall == 0"
19
+
20
+ specify "The car" do
21
+ assuming "the breaks don't fail" => :"G BreakFailure = No" do
22
+ it "does not crash" => :"G distance_to_wall > 0"
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,26 @@
1
+ model :Test do
2
+
3
+ graph :Graph1 do
4
+ var counter: (0..5), init: 0
5
+ states :counting
6
+ transition :counting => :counting do
7
+ precon "counter < 5"
8
+ action "counter := counter + 1"
9
+ end
10
+ end
11
+
12
+ graph :Graph2 do
13
+ states :idle
14
+ end
15
+
16
+ specify "The counter" do
17
+ it "Goes to 0" => :"counter == 0"
18
+ it "Goes to 1" => :"X counter == 1"
19
+ it "Goes to 2" => :"X X counter == 2"
20
+ it "Goes to 3" => :"X X X counter == 3"
21
+ it "Goes to 4" => :"X X X X counter == 4"
22
+ it "Goes to 5" => :"X X X X X counter == 5"
23
+ it "Stays at 5" => :"X X X X X X X X counter == 5"
24
+ it "reaches 5" => :"F counter == 5"
25
+ end
26
+ end
@@ -0,0 +1,19 @@
1
+ model :TestTransitions do
2
+
3
+ transient error :Err1
4
+ transient error :Err2
5
+ transient error :Err3
6
+ transient error :Err4
7
+ transient error :Err5
8
+
9
+ graph :OhNo do
10
+ states :Ok, :Bad, init: :Ok
11
+ transition :Ok => :Bad do
12
+ guard "(Err1 == Yes && Err2 == Yes && Err3 == Yes ) || (Err4 == Yes && Err5 == Yes)"
13
+ end
14
+ end
15
+
16
+ # Expected Cut Sets: { Err1, Err2, Err3 } { Err4, Err5 }
17
+ hazard "Bad" => :"OhNo == Bad"
18
+
19
+ end
@@ -0,0 +1,146 @@
1
+ model :PressureTank do
2
+
3
+ transient error :SwitchOperatedBadly
4
+ persistent error :SensorDead
5
+ # Relay is jammed if it does not open when wanted
6
+ persistent error :Relay1Jammed
7
+ persistent error :Relay2Jammed
8
+
9
+ # S1
10
+ graph :Switch do
11
+ states :Start, :Closed, :Open, init: :Start
12
+
13
+ # Randomly close at some point
14
+ transition :Start => :Start
15
+ transition :Start => :Closed
16
+
17
+ # Open right after closing (unless error)
18
+ transition :Closed => :Open do
19
+ guard "SwitchOperatedBadly == No"
20
+ end
21
+
22
+ # Close again if error
23
+ transition :Open => :Closed do
24
+ guard "SwitchOperatedBadly == Yes"
25
+ end
26
+ end
27
+
28
+ # T
29
+ graph :Timer do
30
+ states :Closed, :Open, :Countdown, init: :Closed
31
+
32
+ var time: (0..60), init: 0
33
+
34
+ countdown_requirement = "Sensor == Closed && ( Switch == Closed || Relay2 == Closed )"
35
+
36
+ transition :Closed => :Countdown do
37
+ guard countdown_requirement
38
+ end
39
+ transition :Countdown => :Countdown do
40
+ guard "#{countdown_requirement} && time < 60"
41
+ action "time := time + 1"
42
+ end
43
+
44
+ transition :Countdown => :Open do
45
+ guard "#{countdown_requirement} && time = 60"
46
+ action "time := 0"
47
+ end
48
+
49
+ transition :Open => :Closed
50
+
51
+ end
52
+
53
+ # D
54
+ graph :Tank do
55
+ states :Functional, :Raptured, init: :Functional
56
+ var pressure: (0..70), init: 0
57
+
58
+ transition :Functional => :Functional do
59
+ guard "Motor == On"
60
+ action "pressure := pressure + 1"
61
+ end
62
+ transition :Functional => :Functional do
63
+ guard "Motor == Off"
64
+ action "pressure := 0"
65
+ end
66
+ transition :Functional => :Raptured do
67
+ guard "Motor == On && pressure > 65"
68
+ action "pressure := 0"
69
+ end
70
+ end
71
+
72
+ # M
73
+ graph :Motor do
74
+ states :Off, :On, init: :Off
75
+
76
+ # Motor turns on/off if relay 2 delivers power or doesn't
77
+ transition :Off => :On do
78
+ guard "Relay2 == Closed"
79
+ end
80
+ transition :On => :Off do
81
+ guard "Relay2 == Open"
82
+ end
83
+ end
84
+
85
+ # S
86
+ graph :Sensor do
87
+ states :Closed, :Open, init: :Closed
88
+
89
+ # Trigger when tank is full (unless dead)
90
+ transition :Closed => :Open do
91
+ guard "pressure >= 60 && SensorDead == No"
92
+ end
93
+ # Stop triggering when tank is empty
94
+ transition :Open => :Closed do
95
+ guard "pressure < 60"
96
+ end
97
+ end
98
+
99
+ # K1
100
+ graph :Relay1 do
101
+ states :Closed, :Open, init: :Open
102
+
103
+ transition :Closed => :Open do
104
+ guard "Timer == Open && Relay1Jammed == No"
105
+ end
106
+ transition :Open => :Closed do
107
+ guard "Timer != Open && Switch == Closed"
108
+ end
109
+ end
110
+
111
+ # K2
112
+ graph :Relay2 do
113
+ states :Closed, :Open, init: :Open
114
+
115
+ close_requirement = "Switch == Closed || ( Relay1 == Closed && Sensor == Closed )"
116
+
117
+ transition :Open => :Closed do
118
+ guard close_requirement
119
+ end
120
+
121
+ transition :Closed => :Open do
122
+ guard "! ( #{close_requirement} ) && Relay2Jammed == No"
123
+ end
124
+
125
+ end
126
+
127
+ specify "The tank" do
128
+ assuming no_errors do
129
+ assuming "the switch is pressed" => :"F Switch == Closed" do
130
+ it "will be pressured" => :"F pressure == 60"
131
+ it "will be depressured again" => :"F (pressure == 60 && F pressure == 0)"
132
+ end
133
+ it "does not rapture" => :"! F Tank == Raptured"
134
+ end
135
+ end
136
+
137
+ specify "The switch" do
138
+ assuming "no fault" => :"G ( SwitchOperatedBadly == No )" do
139
+ it "is only pressed once" => :"G ( Switch == Closed => X (! F Switch == Closed ))"
140
+ end
141
+ end
142
+
143
+ # Expected Cut Sets: { SwitchOperatedBadly } { Relay2Jammed } { SensorDead, Relay1Jammed }
144
+ hazard "The tank raptures" => :"Tank == Raptured"
145
+
146
+ end
@@ -0,0 +1,235 @@
1
+ # Require all module files
2
+ Dir[File.join(__dir__, '*.rb')].sort.each { |file| require file }
3
+
4
+ require 'thor'
5
+ require 'plantuml_builder'
6
+
7
+ module PgVerify
8
+ module Cli
9
+
10
+ class ShowCommand < Thor
11
+
12
+ desc "puml", "Shows the model in PlantUML format"
13
+ method_option :only, :type => :array, repeatable: true
14
+ method_option :hide, :type => :array, repeatable: true
15
+ method_option :script, :type => :string
16
+ def puml()
17
+ script_file = options[:script] || Settings.ruby_dsl.default_script_name
18
+ models = Interpret::PgScript.new.interpret(script_file)
19
+ models.each { |model|
20
+ components = self.class.select_components(options[:only], options[:hide], model)
21
+ puml = Transform::PumlTransformation.new.transform_graph(model, only: components)
22
+ puts puml
23
+ }
24
+ end
25
+
26
+ desc "png", "Shows the model as a PNG image"
27
+ method_option :only, :type => :array, repeatable: true
28
+ method_option :hide, :type => :array, repeatable: true
29
+ method_option :script, :type => :string
30
+ def png()
31
+ script_file = options[:script] || Settings.ruby_dsl.default_script_name
32
+ models = Interpret::PgScript.new.interpret(script_file)
33
+
34
+ models.each { |model|
35
+ components = self.class.select_components(options[:only], options[:hide], model)
36
+ puml = Transform::PumlTransformation.new.transform_graph(model, only: components)
37
+ png = PlantumlBuilder::Formats::PNG.new(puml).load
38
+ out_name = File.basename(script_file, '.*')
39
+ out_name += "-" + model.name.to_s.gsub(/\W+/, '_').downcase + ".png"
40
+ out_path = File.expand_path(out_name, Settings.outdir)
41
+ FileUtils.mkdir_p(Settings.outdir)
42
+ File.binwrite(out_path, png)
43
+ puts "Wrote #{out_path.c_file}"
44
+ }
45
+ end
46
+
47
+ desc "yaml", "Shows the model in YAML format"
48
+ method_option :script, :type => :string
49
+ def yaml()
50
+ script_file = options[:script] || Settings.ruby_dsl.default_script_name
51
+ models = Interpret::PgScript.new.interpret(script_file)
52
+
53
+ models.each { |model|
54
+ hash = Transform::HashTransformation.new.transform_graph(model)
55
+ puts hash.to_yaml
56
+ }
57
+ end
58
+
59
+ desc "json", "Shows the model in Json format"
60
+ method_option :script, :type => :string
61
+ def json()
62
+ script_file = options[:script] || Settings.ruby_dsl.default_script_name
63
+ models = Interpret::PgScript.new.interpret(script_file)
64
+
65
+ models.each { |model|
66
+ hash = Transform::HashTransformation.new.transform_graph(model)
67
+ puts JSON.pretty_generate(hash)
68
+ }
69
+ end
70
+
71
+ desc "nusmv", "Shows the model in NuSMV format"
72
+ method_option :script, :type => :string
73
+ def nusmv()
74
+ script_file = options[:script] || Settings.ruby_dsl.default_script_name
75
+ models = Interpret::PgScript.new.interpret(script_file)
76
+
77
+ models.each { |model|
78
+ nusmv = Transform::NuSmvTransformation.new.transform_graph(model)
79
+ puts nusmv
80
+ }
81
+ end
82
+
83
+ def self.select_components(only_arg, hide_arg, model)
84
+ only = (only_arg || []).flatten.map(&:to_s).map(&:downcase)
85
+ hide = (hide_arg || []).flatten.map(&:to_s).map(&:downcase)
86
+ components = model.components.map(&:name)
87
+ components = components.select { |c| only.include?(c.to_s.downcase) } unless only.empty?
88
+ components = components.reject { |c| hide.include?(c.to_s.downcase) } unless hide.empty?
89
+ return components
90
+ end
91
+
92
+ end
93
+
94
+ class BaseCommand < Thor
95
+
96
+ desc "test", "Test the model specifications"
97
+ method_option :script, :type => :string
98
+ def test()
99
+ script_file = options[:script] || Settings.ruby_dsl.default_script_name
100
+ models = Interpret::PgScript.new.interpret(script_file)
101
+
102
+ models.each { |model|
103
+ results = Shell::LoadingPrompt.while_loading("Running specifications") {
104
+ NuSMV::Runner.new().run_specs(model)
105
+ }
106
+
107
+ results.each { |result|
108
+ stat_string = result.success ? "PASSED".c_success : "FAILED".c_error
109
+ puts "[ #{stat_string} ] #{result.spec.text}"
110
+ puts " #{result.spec.expression.to_s.c_blue}"
111
+ unless result.success
112
+ puts "Here is the trace:".c_red
113
+ trace_s = result.trace.to_s.indented(str: " >> ".c_red)
114
+ puts trace_s + "\n"
115
+ end
116
+ }
117
+ }
118
+ end
119
+
120
+ desc "dcca", "Run the automatic DCCA for hazards of the model"
121
+ method_option :script, :type => :string
122
+ def dcca()
123
+ script_file = options[:script] || Settings.ruby_dsl.default_script_name
124
+ models = Interpret::PgScript.new.interpret(script_file)
125
+
126
+ models.each { |model|
127
+ dcca = Model::DCCA.new(model, NuSMV::Runner.new)
128
+ result = Shell::LoadingPrompt.while_loading("Calculating DCCA for #{model.name.to_s.c_string}") {
129
+ dcca.perform()
130
+ }
131
+ result.each { |hazard, crit_sets|
132
+ s = crit_sets.length == 1 ? "" : "s"
133
+ message = "Hazard #{hazard.text.to_s.c_string} (#{hazard.expression.to_s.c_blue}) "
134
+ message += "has #{crit_sets.length.to_s.c_num} minimal critical cut set#{s}:"
135
+ puts message
136
+ crit_sets.each { |set|
137
+ puts "\t{ #{set.map(&:to_s).map(&:c_blue).join(', ')} }"
138
+ }
139
+ }
140
+ }
141
+ end
142
+
143
+ desc "simulate", "Simulate the model and save each step as an image"
144
+ method_option :script, :type => :string
145
+ method_option :steps, :type => :numeric, default: 10
146
+ method_option :force, :type => :numeric, default: 10
147
+ method_option :random, :type => :boolean, default: false
148
+ method_option :png, :type => :boolean, default: false
149
+ def simulate()
150
+ script_file = options[:script] || Settings.ruby_dsl.default_script_name
151
+ models = Interpret::PgScript.new.interpret(script_file)
152
+ runner = NuSMV::Runner.new
153
+
154
+ models.each { |model|
155
+ trace = Shell::LoadingPrompt.while_loading("Simulating model #{model.name.to_s.c_string}") {
156
+ runner.run_simulation(model, options[:steps], random: options[:random])
157
+ }
158
+
159
+ # Print the trace
160
+ puts trace.to_s
161
+
162
+ next unless options[:png]
163
+
164
+ # Prepare output dir
165
+ out_dir = File.expand_path("simulate-" + model.name.to_s.gsub(/\W+/, '_').downcase, Settings.outdir)
166
+ FileUtils.mkdir_p(out_dir)
167
+
168
+ # Generate images
169
+ Shell::LoadingPrompt.while_loading("Rendering states") { |printer|
170
+ trace.states.each_with_index { |variable_state, index|
171
+ printer.printl("Step #{index + 1}/#{trace.states.length}")
172
+ puml = Transform::PumlTransformation.new.transform_graph(model, variable_state: variable_state)
173
+ png = PlantumlBuilder::Formats::PNG.new(puml).load
174
+ out_path = File.expand_path("step-#{index}.png", out_dir)
175
+ File.binwrite(out_path, png)
176
+ }
177
+ }
178
+ puts "Wrote #{trace.states.length.to_s.c_num} files to #{out_dir.c_file}"
179
+ }
180
+
181
+ end
182
+
183
+ desc "init", "Initialize a new pg-verify project"
184
+ method_option :directory, :type => :string
185
+ def init()
186
+
187
+ target = options[:directory].blank? ? Dir.pwd() : File.expand_path(options[:directory])
188
+ if Dir.exist?(target) && Dir.entries(target).size > 2
189
+ puts "The target directory #{target.c_file} isn't empty!"
190
+ exit 1
191
+ end
192
+
193
+ # Create target dir
194
+ FileUtils.mkdir_p(target)
195
+
196
+ # Copy files to target
197
+ template_dir = File.join(PgVerify.root, "data", "project-template")
198
+ files = Dir.glob(File.join(template_dir, '**', '*'), File::FNM_DOTMATCH).select { |f| File.file?(f) }
199
+ files.each { |f|
200
+ target_file = File.join(target, f.sub(template_dir, ""))
201
+ target_file = target_file.gsub(".resource", "")
202
+ FileUtils.mkdir_p(File.dirname(target_file))
203
+ FileUtils.cp(f, target_file) unless File.basename(f) == ".keep"
204
+ }
205
+ # Copy the actual default config into the project as that
206
+ # will contain all keys and should be commented
207
+ FileUtils.cp(File.join(PgVerify.root, "data", "config", "pg-verify.yml"), File.join(target, ".pg-verify.yml"))
208
+
209
+ # Initialize git project
210
+ Dir.chdir(target) {
211
+ Core::CMDRunner.run_cmd("git init")
212
+ Core::CMDRunner.run_cmd("git add .")
213
+ Core::CMDRunner.run_cmd("git commit -m \"Initial Commit\"")
214
+ }
215
+
216
+ puts "Successfully initialized project at #{target.c_file}!"
217
+ puts "You can read the #{'README.md'.c_file} to get started."
218
+ puts "Run #{'pg-verify doctor'.c_blue} and follow the instructions to set up your environment!"
219
+ end
220
+
221
+ desc "doctor", "Check for common problems"
222
+ def doctor()
223
+ Doctor.check()
224
+ end
225
+
226
+ desc "show", "Show the program graph multiple different ways"
227
+ subcommand 'show', ShowCommand
228
+
229
+ # Make Thor exit with non-0 in case of errors
230
+ def self.exit_on_failure?(); true end
231
+
232
+ end
233
+
234
+ end
235
+ end