sfplanner 0.1.3 → 0.1.4
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.
- data/README.md +8 -0
- data/VERSION +1 -1
- data/bin/sfplanner +31 -14
- data/bin/sfw2graph +7 -127
- data/lib/sfplanner/graph.rb +132 -0
- data/lib/sfplanner/planner.rb +49 -18
- data/lib/sfplanner.rb +1 -0
- data/sfplanner.gemspec +1 -0
- metadata +5 -3
data/README.md
CHANGED
@@ -13,6 +13,13 @@ Click [here](https://github.com/herry13/nuri/wiki/SFP-language), for more detail
|
|
13
13
|
This is a spin-out project from [Nuri](https://github.com/herry13/nuri).
|
14
14
|
|
15
15
|
|
16
|
+
Features
|
17
|
+
--------
|
18
|
+
- Automatically generating a sequential or partial-order (parallel) plan.
|
19
|
+
- Use JSON as the standard format of the plan.
|
20
|
+
- Generate an image file that illustrates the graph of the plan.
|
21
|
+
|
22
|
+
|
16
23
|
To install
|
17
24
|
----------
|
18
25
|
|
@@ -22,6 +29,7 @@ To install
|
|
22
29
|
Requirements
|
23
30
|
------------
|
24
31
|
- Ruby (>= 1.8.7)
|
32
|
+
- Graphviz
|
25
33
|
- Rubygems
|
26
34
|
- sfp (>= 0.3.0)
|
27
35
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.4
|
data/bin/sfplanner
CHANGED
@@ -15,9 +15,11 @@ Usage:
|
|
15
15
|
where [options] are:
|
16
16
|
EOS
|
17
17
|
|
18
|
-
opt :parallel, "Generate a parallel (partial-order) plan, instead of sequential."
|
19
|
-
opt :json_input, "Input is in JSON format"
|
20
|
-
opt :
|
18
|
+
opt :parallel, "Generate a parallel (partial-order) plan, instead of sequential.", :short => '-l'
|
19
|
+
opt :json_input, "Input is in JSON format", :short => '-j'
|
20
|
+
opt :pretty_json, "Print the plan in pretty JSON format", :short => '-r'
|
21
|
+
opt :image, "Generate a graph image (PNG) of the plan"
|
22
|
+
opt :output, "Output file path", :default => ''
|
21
23
|
end
|
22
24
|
|
23
25
|
def parse(filepath)
|
@@ -27,20 +29,35 @@ def parse(filepath)
|
|
27
29
|
parser
|
28
30
|
end
|
29
31
|
|
30
|
-
|
31
|
-
|
32
|
-
if filepath != ''
|
32
|
+
if ARGV[0]
|
33
33
|
planner = Sfp::Planner.new
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
34
|
+
opts[:file] = ARGV[0]
|
35
|
+
opts[:json] = (!opts[:pretty_json] and !opts[:dot])
|
36
|
+
opts[:dot] = true if opts[:image]
|
37
|
+
|
38
|
+
result = planner.solve(opts)
|
39
|
+
if !opts[:image]
|
40
|
+
if opts[:output].length > 0
|
41
|
+
File.open(opts[:output], 'w') { |f|
|
42
|
+
f.write(result)
|
43
|
+
f.flush
|
44
|
+
}
|
45
|
+
else
|
46
|
+
puts result
|
47
|
+
end
|
41
48
|
else
|
42
|
-
|
49
|
+
if opts[:output].length <= 0
|
50
|
+
parts = ARGV[0].split('/')
|
51
|
+
src_file = parts[parts.length-1]
|
52
|
+
parts = src_file.split('.')
|
53
|
+
opts[:output] = src_file[0, src_file.length-1-parts[parts.length-1].length] if parts.length >= 2
|
54
|
+
opts[:output] += '.png'
|
55
|
+
end
|
56
|
+
if !Sfp::Graph.dot2image(result, opts[:output])
|
57
|
+
$stderr.puts "Cannot generate the image graph!"
|
58
|
+
end
|
43
59
|
end
|
60
|
+
|
44
61
|
else
|
45
62
|
Trollop::help
|
46
63
|
end
|
data/bin/sfw2graph
CHANGED
@@ -3,134 +3,14 @@
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'json'
|
5
5
|
|
6
|
-
module
|
7
|
-
module Planner
|
8
|
-
module Graph
|
9
|
-
ActionColor = 'white'
|
10
|
-
ActionLabelWithParameters = false
|
11
|
-
|
12
|
-
def self.clean(value)
|
13
|
-
return value[2, value.length-2] if value[0,2] == '$.'
|
14
|
-
return value
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.get_label(action, withparameters=true)
|
18
|
-
label = clean(action["name"])
|
19
|
-
if withparameters and ActionLabelWithParameters
|
20
|
-
label += "("
|
21
|
-
if action["parameters"].length > 0
|
22
|
-
action["parameters"].each { |key,value|
|
23
|
-
label += "#{clean(key)}=#{clean(value.to_s)},"
|
24
|
-
}
|
25
|
-
label.chop!
|
26
|
-
end
|
27
|
-
label += ')'
|
28
|
-
end
|
29
|
-
return label
|
30
|
-
end
|
31
|
-
|
32
|
-
def self.partial2dot(json)
|
33
|
-
dot = "digraph {\n"
|
34
|
-
|
35
|
-
dot += "init_state [label=\"\", shape=doublecircle, fixedsize=true, width=0.35];\n"
|
36
|
-
dot += "final_state [label=\"\", shape=doublecircle, style=filled, fillcolor=black, fixedsize=true, width=0.35];\n"
|
37
|
-
last_actions = Hash.new
|
38
|
-
json["workflow"].each { |action|
|
39
|
-
dot += "#{action["id"]}[label=\"#{get_label(action)}\", shape=rect, style=filled, fillcolor=#{ActionColor}];\n"
|
40
|
-
last_actions[action["id"].to_s] = action
|
41
|
-
}
|
42
|
-
|
43
|
-
json["workflow"].each { |action|
|
44
|
-
has_predecessor = false
|
45
|
-
action["predecessors"].each { |prevId|
|
46
|
-
dot += "#{prevId} -> #{action["id"]};\n"
|
47
|
-
has_predecessor = true
|
48
|
-
last_actions.delete(prevId.to_s)
|
49
|
-
}
|
50
|
-
if not has_predecessor
|
51
|
-
dot += "init_state -> #{action["id"]};\n"
|
52
|
-
end
|
53
|
-
}
|
54
|
-
|
55
|
-
last_actions.each { |id,action|
|
56
|
-
dot += "#{id} -> final_state;\n"
|
57
|
-
}
|
58
|
-
|
59
|
-
dot += "}"
|
60
|
-
|
61
|
-
return dot
|
62
|
-
end
|
63
|
-
|
64
|
-
def self.stage2dot(json)
|
65
|
-
dot = "digraph {\n"
|
66
|
-
|
67
|
-
dot += "init_state [label=\"\", shape=doublecircle, fixedsize=true, width=0.35];\n"
|
68
|
-
dot += "final_state [label=\"\", shape=doublecircle, style=filled, fillcolor=black, fixedsize=true, width=0.35];\n"
|
69
|
-
index = 0
|
70
|
-
prevState = "init_state"
|
71
|
-
json["workflow"].each { |stage|
|
72
|
-
id = 0
|
73
|
-
stage.each { |action|
|
74
|
-
dot += "a" + index.to_s + "a" + id.to_s + '[label="' + get_label(action) + '", shape=rect]' + ";\n"
|
75
|
-
dot += prevState + " -> a" + index.to_s + "a" + id.to_s + ";\n"
|
76
|
-
id += 1
|
77
|
-
}
|
78
|
-
if index < json["workflow"].length-1
|
79
|
-
dot += "state" + index.to_s + ' [label="", shape=circle, fixedsize=true, width=0.35]' + ";\n"
|
80
|
-
prevState = "state" + index.to_s
|
81
|
-
id = 0
|
82
|
-
stage.each { |action|
|
83
|
-
dot += "a" + index.to_s + "a" + id.to_s + " -> " + prevState + ";\n"
|
84
|
-
id += 1
|
85
|
-
}
|
86
|
-
else
|
87
|
-
id = 0
|
88
|
-
stage.each { |action|
|
89
|
-
dot += "a" + index.to_s + "a" + id.to_s + " -> final_state;\n"
|
90
|
-
id += 1
|
91
|
-
}
|
92
|
-
end
|
93
|
-
index += 1
|
94
|
-
}
|
95
|
-
dot += "}"
|
96
|
-
return dot
|
97
|
-
end
|
98
|
-
|
99
|
-
def self.sequential2dot(json)
|
100
|
-
dot = "digraph {\n"
|
101
|
-
|
102
|
-
dot += "init_state [label=\"\", shape=doublecircle, fixedsize=true, width=0.35];\n"
|
103
|
-
dot += "final_state [label=\"\", shape=doublecircle, style=filled, fillcolor=black, fixedsize=true, width=0.35];\n"
|
104
|
-
id = 0
|
105
|
-
json["workflow"].each { |action|
|
106
|
-
dot += id.to_s + '[label="' + get_label(action) + '", shape=rect]' + ";\n"
|
107
|
-
id += 1
|
108
|
-
}
|
109
|
-
|
110
|
-
id = 0
|
111
|
-
prevActionId = nil
|
112
|
-
json["workflow"].each { |action|
|
113
|
-
if id == 0
|
114
|
-
dot += "init_state -> " + id.to_s + ";\n"
|
115
|
-
elsif id == json["workflow"].length-1
|
116
|
-
dot += id.to_s + " -> final_state;\n"
|
117
|
-
end
|
118
|
-
if prevActionId != nil
|
119
|
-
dot += prevActionId.to_s + " -> " + id.to_s + ";\n"
|
120
|
-
end
|
121
|
-
prevActionId = id
|
122
|
-
id += 1
|
123
|
-
}
|
124
|
-
dot += "}"
|
125
|
-
return dot
|
126
|
-
end
|
127
|
-
end
|
128
|
-
end
|
6
|
+
module Sfp
|
129
7
|
end
|
130
8
|
|
9
|
+
require File.expand_path('../../lib/sfplanner/graph.rb', __FILE__)
|
10
|
+
|
131
11
|
def main
|
132
12
|
if ARGV.length < 1
|
133
|
-
puts "
|
13
|
+
puts "Convert SFP plan to an image graph with Graphviz (dot).\n\nUsage: sfw2dot.rb <input-file> [output-file]\n\n"
|
134
14
|
exit
|
135
15
|
end
|
136
16
|
|
@@ -141,11 +21,11 @@ def main
|
|
141
21
|
dot = ""
|
142
22
|
case json["type"]
|
143
23
|
when 'partial-order', 'parallel'
|
144
|
-
dot =
|
24
|
+
dot = Sfp::Graph::partial2dot(json)
|
145
25
|
when 'sequential'
|
146
|
-
dot =
|
26
|
+
dot = Sfp::Graph::sequential2dot(json)
|
147
27
|
when 'stage'
|
148
|
-
dot =
|
28
|
+
dot = Sfp::Graph::stage2dot(json)
|
149
29
|
else
|
150
30
|
throw Exception, "Unrecognised type of workflow: #{json['type']}"
|
151
31
|
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
module Sfp::Graph
|
2
|
+
ActionColor = 'white'
|
3
|
+
ActionLabelWithParameters = false
|
4
|
+
|
5
|
+
def self.dot2image(dot, image_file)
|
6
|
+
dot_file = "/tmp/#{Time.now.getutc.to_i}.dot"
|
7
|
+
File.open(dot_file, 'w') { |f|
|
8
|
+
f.write(dot)
|
9
|
+
f.flush
|
10
|
+
}
|
11
|
+
!!system("dot -Tpng -o #{image_file} #{dot_file}")
|
12
|
+
ensure
|
13
|
+
File.delete(dot_file) if File.exist?(dot_file)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.clean(value)
|
17
|
+
return value[2, value.length-2] if value[0,2] == '$.'
|
18
|
+
return value
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.get_label(action, withparameters=true)
|
22
|
+
label = clean(action["name"])
|
23
|
+
if withparameters and ActionLabelWithParameters
|
24
|
+
label += "("
|
25
|
+
if action["parameters"].length > 0
|
26
|
+
action["parameters"].each { |key,value|
|
27
|
+
label += "#{clean(key)}=#{clean(value.to_s)},"
|
28
|
+
}
|
29
|
+
label.chop!
|
30
|
+
end
|
31
|
+
label += ')'
|
32
|
+
end
|
33
|
+
return label
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.partial2dot(json)
|
37
|
+
dot = "digraph {\n"
|
38
|
+
|
39
|
+
dot += "init_state [label=\"\", shape=doublecircle, fixedsize=true, width=0.35];\n"
|
40
|
+
dot += "final_state [label=\"\", shape=doublecircle, style=filled, fillcolor=black, fixedsize=true, width=0.35];\n"
|
41
|
+
last_actions = Hash.new
|
42
|
+
json["workflow"].each { |action|
|
43
|
+
dot += "#{action["id"]}[label=\"#{get_label(action)}\", shape=rect, style=filled, fillcolor=#{ActionColor}];\n"
|
44
|
+
last_actions[action["id"].to_s] = action
|
45
|
+
}
|
46
|
+
|
47
|
+
json["workflow"].each { |action|
|
48
|
+
has_predecessor = false
|
49
|
+
action["predecessors"].each { |prevId|
|
50
|
+
dot += "#{prevId} -> #{action["id"]};\n"
|
51
|
+
has_predecessor = true
|
52
|
+
last_actions.delete(prevId.to_s)
|
53
|
+
}
|
54
|
+
if not has_predecessor
|
55
|
+
dot += "init_state -> #{action["id"]};\n"
|
56
|
+
end
|
57
|
+
}
|
58
|
+
|
59
|
+
last_actions.each { |id,action|
|
60
|
+
dot += "#{id} -> final_state;\n"
|
61
|
+
}
|
62
|
+
|
63
|
+
dot += "}"
|
64
|
+
|
65
|
+
return dot
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.stage2dot(json)
|
69
|
+
dot = "digraph {\n"
|
70
|
+
|
71
|
+
dot += "init_state [label=\"\", shape=doublecircle, fixedsize=true, width=0.35];\n"
|
72
|
+
dot += "final_state [label=\"\", shape=doublecircle, style=filled, fillcolor=black, fixedsize=true, width=0.35];\n"
|
73
|
+
index = 0
|
74
|
+
prevState = "init_state"
|
75
|
+
json["workflow"].each { |stage|
|
76
|
+
id = 0
|
77
|
+
stage.each { |action|
|
78
|
+
dot += "a" + index.to_s + "a" + id.to_s + '[label="' + get_label(action) + '", shape=rect]' + ";\n"
|
79
|
+
dot += prevState + " -> a" + index.to_s + "a" + id.to_s + ";\n"
|
80
|
+
id += 1
|
81
|
+
}
|
82
|
+
if index < json["workflow"].length-1
|
83
|
+
dot += "state" + index.to_s + ' [label="", shape=circle, fixedsize=true, width=0.35]' + ";\n"
|
84
|
+
prevState = "state" + index.to_s
|
85
|
+
id = 0
|
86
|
+
stage.each { |action|
|
87
|
+
dot += "a" + index.to_s + "a" + id.to_s + " -> " + prevState + ";\n"
|
88
|
+
id += 1
|
89
|
+
}
|
90
|
+
else
|
91
|
+
id = 0
|
92
|
+
stage.each { |action|
|
93
|
+
dot += "a" + index.to_s + "a" + id.to_s + " -> final_state;\n"
|
94
|
+
id += 1
|
95
|
+
}
|
96
|
+
end
|
97
|
+
index += 1
|
98
|
+
}
|
99
|
+
dot += "}"
|
100
|
+
return dot
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.sequential2dot(json)
|
104
|
+
dot = "digraph {\n"
|
105
|
+
|
106
|
+
dot += "init_state [label=\"\", shape=doublecircle, fixedsize=true, width=0.35];\n"
|
107
|
+
dot += "final_state [label=\"\", shape=doublecircle, style=filled, fillcolor=black, fixedsize=true, width=0.35];\n"
|
108
|
+
id = 0
|
109
|
+
json["workflow"].each { |action|
|
110
|
+
dot += id.to_s + '[label="' + get_label(action) + '", shape=rect]' + ";\n"
|
111
|
+
id += 1
|
112
|
+
}
|
113
|
+
|
114
|
+
id = 0
|
115
|
+
prevActionId = nil
|
116
|
+
json["workflow"].each { |action|
|
117
|
+
if id == 0
|
118
|
+
dot += "init_state -> " + id.to_s + ";\n"
|
119
|
+
elsif id == json["workflow"].length-1
|
120
|
+
dot += id.to_s + " -> final_state;\n"
|
121
|
+
end
|
122
|
+
if prevActionId != nil
|
123
|
+
dot += prevActionId.to_s + " -> " + id.to_s + ";\n"
|
124
|
+
end
|
125
|
+
prevActionId = id
|
126
|
+
id += 1
|
127
|
+
}
|
128
|
+
dot += "}"
|
129
|
+
return dot
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
data/lib/sfplanner/planner.rb
CHANGED
@@ -26,20 +26,26 @@ module Sfp
|
|
26
26
|
attr_accessor :debug
|
27
27
|
attr_reader :parser
|
28
28
|
|
29
|
+
# @param all parameters are passed to Sfp::Parser#initialize method
|
30
|
+
#
|
29
31
|
def initialize(params={})
|
30
32
|
@parser = Sfp::Parser.new(params)
|
31
33
|
@debug = Debug
|
32
34
|
end
|
33
35
|
|
34
|
-
# @param :string
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
36
|
+
# @param :string => SFP task in string
|
37
|
+
# :sfp => SFP task in Hash data structure
|
38
|
+
# :file => SFP task in file with specified path
|
39
|
+
# :json_input => SFP task in JSON format
|
40
|
+
# :sas_plan => if true then return a raw SAS plan
|
41
|
+
# :parallel => if true then return a parallel (partial-order) plan,
|
39
42
|
# if false or nil then return a sequential plan
|
40
|
-
#
|
41
|
-
#
|
42
|
-
#
|
43
|
+
# :json => if true then return the plan in JSON
|
44
|
+
# :pretty_json => if true then return in pretty JSON
|
45
|
+
# :bsig => if true then return the solution plan as a BSig model
|
46
|
+
#
|
47
|
+
# @return if solution plan is found then returns a JSON or Hash
|
48
|
+
# otherwise return nil
|
43
49
|
#
|
44
50
|
def solve(params={})
|
45
51
|
if params[:string].is_a?(String)
|
@@ -48,7 +54,7 @@ module Sfp
|
|
48
54
|
@parser.root = params[:sfp]
|
49
55
|
elsif params[:file].is_a?(String)
|
50
56
|
raise Exception, "File not found: #{params[:file]}" if not File.exist?(params[:file])
|
51
|
-
if params[:
|
57
|
+
if params[:json_input]
|
52
58
|
@parser.root = json_to_sfp(JSON[File.read(params[:file])])
|
53
59
|
else
|
54
60
|
@parser.home_dir = File.expand_path(File.dirname(params[:file]))
|
@@ -67,10 +73,15 @@ module Sfp
|
|
67
73
|
end
|
68
74
|
end
|
69
75
|
|
70
|
-
#
|
71
|
-
#
|
72
|
-
# @param :
|
73
|
-
#
|
76
|
+
# Return Behavioural Signature (BSig) model of previously generated plan.
|
77
|
+
#
|
78
|
+
# @param :parallel => if true then return a parallel (partial-order) plan,
|
79
|
+
# if false or nil then return a sequential plan
|
80
|
+
# :json => if true then return the plan in JSON
|
81
|
+
# :pretty_json => if true then return in pretty JSON
|
82
|
+
#
|
83
|
+
# @return if solution BSig model is found then returns a JSON or Hash,
|
84
|
+
# otherwise return nil
|
74
85
|
#
|
75
86
|
def to_bsig(params={})
|
76
87
|
raise Exception, "Conformant task is not supported yet" if @parser.conformant
|
@@ -81,8 +92,12 @@ module Sfp
|
|
81
92
|
(params[:pretty_json] ? JSON.pretty_generate(bsig) : bsig))
|
82
93
|
end
|
83
94
|
|
84
|
-
#
|
85
|
-
#
|
95
|
+
# Return the final state if the plan is executed.
|
96
|
+
#
|
97
|
+
# @param :json => if true then return in JSON
|
98
|
+
# :pretty_json => if true then return in pretty JSON
|
99
|
+
#
|
100
|
+
# @return [Hash]
|
86
101
|
#
|
87
102
|
def final_state(params={})
|
88
103
|
return nil if @plan.nil?
|
@@ -92,6 +107,14 @@ module Sfp
|
|
92
107
|
end
|
93
108
|
|
94
109
|
protected
|
110
|
+
def to_dot(plan)
|
111
|
+
if plan['type'] == 'parallel'
|
112
|
+
Sfp::Graph.partial2dot(self.get_parallel_plan)
|
113
|
+
else
|
114
|
+
Sfp::Graph.sequential2dot(self.get_sequential_plan)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
95
118
|
def json_to_sfp(json)
|
96
119
|
json.accept(Sfp::Visitor::SfpGenerator.new(json))
|
97
120
|
json.each do |key,val|
|
@@ -177,8 +200,16 @@ module Sfp
|
|
177
200
|
return to_bsig(params) if params[:bsig]
|
178
201
|
|
179
202
|
plan = (params[:parallel] ? self.get_parallel_plan : self.get_sequential_plan)
|
180
|
-
|
181
|
-
|
203
|
+
|
204
|
+
if params[:dot]
|
205
|
+
to_dot(plan)
|
206
|
+
elsif params[:json]
|
207
|
+
JSON.generate(plan)
|
208
|
+
elsif params[:pretty_json]
|
209
|
+
JSON.pretty_generate(plan)
|
210
|
+
else
|
211
|
+
plan
|
212
|
+
end
|
182
213
|
end
|
183
214
|
|
184
215
|
def bsig_template
|
@@ -494,7 +525,7 @@ module Sfp
|
|
494
525
|
@dir = dir
|
495
526
|
@sas_file = sas_file
|
496
527
|
@plan_file = plan_file
|
497
|
-
@heuristics_order = ['
|
528
|
+
@heuristics_order = ['ff2', 'cea2', 'autotune12', 'autotune22']
|
498
529
|
@heuristics_order = ENV['SFPLANNER_MIXED_HEURISTICS'].split(',') if ENV['SFPLANNER_MIXED_HEURISTICS']
|
499
530
|
@continue = continue
|
500
531
|
@continue = true if ENV['SFPLANNER_MIXED_CONTINUE']
|
data/lib/sfplanner.rb
CHANGED
data/sfplanner.gemspec
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sfplanner
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ date: 2013-08-13 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sfp
|
16
|
-
requirement: &
|
16
|
+
requirement: &16262980 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,13 +21,14 @@ dependencies:
|
|
21
21
|
version: 0.3.12
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *16262980
|
25
25
|
description: A Ruby gem that provides a Ruby API and a script to the SFP planner.
|
26
26
|
This planner can automatically generate a plan that solves a planning problem written
|
27
27
|
in SFP language.
|
28
28
|
email: herry13@gmail.com
|
29
29
|
executables:
|
30
30
|
- sfplanner
|
31
|
+
- sfw2graph
|
31
32
|
extensions: []
|
32
33
|
extra_rdoc_files: []
|
33
34
|
files:
|
@@ -42,6 +43,7 @@ files:
|
|
42
43
|
- bin/solver/macos/downward
|
43
44
|
- bin/solver/macos/preprocess
|
44
45
|
- lib/sfplanner.rb
|
46
|
+
- lib/sfplanner/graph.rb
|
45
47
|
- lib/sfplanner/planner.rb
|
46
48
|
- lib/sfplanner/sas.rb
|
47
49
|
- sfplanner.gemspec
|