netlinx-workspace 0.3.0 → 1.0.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.
- checksums.yaml +4 -4
- data/README.md +206 -6
- data/bin/netlinx-workspace +27 -0
- data/doc/NetLinx.html +18 -7
- data/doc/NetLinx/Compile.html +15 -4
- data/doc/NetLinx/Compile/Extension.html +15 -4
- data/doc/NetLinx/Compile/Extension/APW.html +8 -8
- data/doc/NetLinx/Project.html +334 -90
- data/doc/NetLinx/Rake.html +128 -0
- data/doc/NetLinx/Rake/Workspace.html +128 -0
- data/doc/NetLinx/Rake/Workspace/CreateWorkspaceConfig.html +387 -0
- data/doc/NetLinx/Rake/Workspace/GenerateAPW.html +371 -0
- data/doc/NetLinx/System.html +1341 -217
- data/doc/NetLinx/SystemFile.html +454 -51
- data/doc/NetLinx/Workspace.html +434 -69
- data/doc/NetLinx/Workspace/YAML.html +398 -0
- data/doc/_index.html +66 -4
- data/doc/class_list.html +6 -2
- data/doc/file.README.html +218 -10
- data/doc/file.license.html +4 -4
- data/doc/file_list.html +5 -1
- data/doc/frames.html +1 -1
- data/doc/index.html +218 -10
- data/doc/js/full_list.js +4 -1
- data/doc/method_list.html +171 -23
- data/doc/top-level-namespace.html +3 -3
- data/lib/netlinx/compile/extension/apw.rb +3 -0
- data/lib/netlinx/rake/workspace.rb +14 -0
- data/lib/netlinx/rake/workspace/create_workspace_config.rb +49 -0
- data/lib/netlinx/rake/workspace/generate_apw.rb +41 -0
- data/lib/netlinx/workspace.rb +58 -14
- data/lib/netlinx/workspace/project.rb +107 -0
- data/lib/netlinx/workspace/system.rb +226 -0
- data/lib/netlinx/workspace/system_file.rb +98 -0
- data/lib/netlinx/workspace/yaml.rb +217 -0
- data/license.txt +1 -1
- metadata +53 -28
- data/lib/netlinx-workspace.rb +0 -1
- data/lib/netlinx/project.rb +0 -83
- data/lib/netlinx/system.rb +0 -122
- data/lib/netlinx/system_file.rb +0 -36
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/tasklib'
|
3
|
+
|
4
|
+
module NetLinx
|
5
|
+
module Rake
|
6
|
+
module Workspace
|
7
|
+
|
8
|
+
# Generate .apw workspace file from yaml config.
|
9
|
+
class GenerateAPW < ::Rake::TaskLib
|
10
|
+
|
11
|
+
attr_accessor :name
|
12
|
+
|
13
|
+
def initialize name = :generate_apw
|
14
|
+
@name = name
|
15
|
+
yield self if block_given?
|
16
|
+
|
17
|
+
desc "Generate .apw workspace file from yaml config."
|
18
|
+
|
19
|
+
task(name) do
|
20
|
+
require 'netlinx/workspace'
|
21
|
+
|
22
|
+
workspace_file = 'workspace.config.yaml'
|
23
|
+
|
24
|
+
unless File.exists? workspace_file
|
25
|
+
puts "File not found: #{workspace_file}"
|
26
|
+
next
|
27
|
+
end
|
28
|
+
|
29
|
+
NetLinx::Workspace::YAML.parse_file(workspace_file).tap do |workspace|
|
30
|
+
return unless workspace.name
|
31
|
+
File.open("#{workspace.name.strip}.apw", 'w') do |f|
|
32
|
+
f.write workspace.to_xml
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/netlinx/workspace.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
|
+
# Gem convenience includes.
|
2
|
+
require_relative 'workspace/yaml'
|
3
|
+
|
4
|
+
# File includes.
|
1
5
|
require 'rexml/document'
|
2
|
-
|
6
|
+
require_relative 'workspace/project'
|
3
7
|
|
4
8
|
module NetLinx
|
5
|
-
|
6
9
|
# A NetLinx Studio workspace.
|
7
10
|
# Collection of projects.
|
8
11
|
# Workspace -> Project -> System
|
@@ -26,17 +29,19 @@ module NetLinx
|
|
26
29
|
nil
|
27
30
|
end
|
28
31
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
+
# @option kwargs [String] :name ('') Workspace name.
|
33
|
+
# @option kwargs [String] :description ('')
|
34
|
+
def initialize **kwargs
|
35
|
+
@name = kwargs.fetch :name, ''
|
36
|
+
@description = kwargs.fetch :description, ''
|
32
37
|
@projects = []
|
33
38
|
|
34
|
-
@file =
|
39
|
+
@file = kwargs.fetch :file, nil
|
35
40
|
load_workspace @file if @file
|
36
41
|
end
|
37
42
|
|
38
43
|
# Alias to add a project.
|
39
|
-
def <<
|
44
|
+
def << project
|
40
45
|
@projects << project
|
41
46
|
project.workspace = self
|
42
47
|
end
|
@@ -51,8 +56,8 @@ module NetLinx
|
|
51
56
|
File.dirname @file if @file
|
52
57
|
end
|
53
58
|
|
54
|
-
#
|
55
|
-
def include?
|
59
|
+
# @return true if the workspace contains the specified file.
|
60
|
+
def include? file
|
56
61
|
included = false
|
57
62
|
|
58
63
|
projects.each do |project|
|
@@ -74,10 +79,45 @@ module NetLinx
|
|
74
79
|
compiler_results
|
75
80
|
end
|
76
81
|
|
82
|
+
# @return [REXML::Element] an XML element representing this workspace.
|
83
|
+
def to_xml_element
|
84
|
+
REXML::Element.new('Workspace').tap do |workspace|
|
85
|
+
workspace.attributes['CurrentVersion'] = '4.0'
|
86
|
+
|
87
|
+
workspace.add_element('Identifier').tap { |e| e.text = name }
|
88
|
+
workspace.add_element('CreateVersion').tap { |e| e.text = '4.0' }
|
89
|
+
workspace.add_element('Comments').tap { |e| e.text = description }
|
90
|
+
|
91
|
+
@projects.each { |project| workspace << project.to_xml_element }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# @return [String] an XML string representing this workspace.
|
96
|
+
#
|
97
|
+
# @todo REXML bug forces :indent to be -1 or else erroneous line feeds are added.
|
98
|
+
# https://bugs.ruby-lang.org/issues/10864
|
99
|
+
def to_xml indent: -1
|
100
|
+
str = '<?xml version="1.0" encoding="UTF-8"?>' + "\n"
|
101
|
+
|
102
|
+
REXML::Document.new.tap do |doc|
|
103
|
+
doc << to_xml_element
|
104
|
+
doc.write output: str, indent: indent
|
105
|
+
end
|
106
|
+
|
107
|
+
str + "\n"
|
108
|
+
end
|
109
|
+
|
110
|
+
# Generate a {NetLinx::Workspace} from an XML string.
|
111
|
+
# @return self
|
112
|
+
def parse_xml string
|
113
|
+
parse_xml_element REXML::Document.new(string)
|
114
|
+
self
|
115
|
+
end
|
116
|
+
|
77
117
|
private
|
78
118
|
|
79
119
|
# Load the workspace from a given NetLinx Studio .apw file.
|
80
|
-
def load_workspace
|
120
|
+
def load_workspace file
|
81
121
|
raise LoadError, "File does not exist at:\n#{file}" unless File.exists? file
|
82
122
|
|
83
123
|
doc = nil
|
@@ -85,12 +125,16 @@ module NetLinx
|
|
85
125
|
doc = REXML::Document.new f
|
86
126
|
end
|
87
127
|
|
128
|
+
parse_xml_element doc
|
129
|
+
end
|
130
|
+
|
131
|
+
def parse_xml_element root
|
88
132
|
# Load workspace params.
|
89
|
-
@name =
|
90
|
-
@description =
|
91
|
-
|
133
|
+
@name = root.elements['/Workspace/Identifier'].text.strip || ''
|
134
|
+
@description = root.elements['/Workspace/Comments'].text || ''
|
135
|
+
|
92
136
|
# Load projects.
|
93
|
-
|
137
|
+
root.each_element '/Workspace/Project' do |e|
|
94
138
|
project = NetLinx::Project.new \
|
95
139
|
element: e,
|
96
140
|
workspace: self
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
require_relative 'system'
|
3
|
+
|
4
|
+
module NetLinx
|
5
|
+
# A collection of NetLinx systems.
|
6
|
+
# Workspace -> Project -> System
|
7
|
+
class Project
|
8
|
+
# A reference to the project's parent workspace.
|
9
|
+
attr_accessor :workspace
|
10
|
+
attr_accessor :name
|
11
|
+
attr_accessor :description
|
12
|
+
attr_accessor :dealer
|
13
|
+
attr_accessor :designer
|
14
|
+
attr_accessor :sales_order
|
15
|
+
attr_accessor :purchase_order
|
16
|
+
attr_accessor :systems
|
17
|
+
|
18
|
+
# @option kwargs [NetLinx::Workspace] :workspace This system's parent workspace node.
|
19
|
+
# @option kwargs [String] :name ('') Project name.
|
20
|
+
# @option kwargs [String] :description ('')
|
21
|
+
# @option kwargs [String] :dealer ('')
|
22
|
+
# @option kwargs [String] :designer ('')
|
23
|
+
# @option kwargs [String] :sales_order ('')
|
24
|
+
# @option kwargs [String] :purchase_order ('')
|
25
|
+
def initialize **kwargs
|
26
|
+
@workspace = kwargs.fetch :workspace, nil
|
27
|
+
|
28
|
+
@name = kwargs.fetch :name, ''
|
29
|
+
@description = kwargs.fetch :description, ''
|
30
|
+
@dealer = kwargs.fetch :dealer, ''
|
31
|
+
@designer = kwargs.fetch :designer, ''
|
32
|
+
@sales_order = kwargs.fetch :sales_order, ''
|
33
|
+
@purchase_order = kwargs.fetch :purchase_order, ''
|
34
|
+
|
35
|
+
@systems = []
|
36
|
+
|
37
|
+
project_element = kwargs.fetch :element, nil
|
38
|
+
parse_xml_element project_element if project_element
|
39
|
+
end
|
40
|
+
|
41
|
+
# Alias to add a system.
|
42
|
+
def << system
|
43
|
+
@systems << system
|
44
|
+
system.project = self
|
45
|
+
end
|
46
|
+
|
47
|
+
# @return the project name.
|
48
|
+
def to_s
|
49
|
+
@name
|
50
|
+
end
|
51
|
+
|
52
|
+
# @return true if the project contains the specified file.
|
53
|
+
def include? file
|
54
|
+
included = false
|
55
|
+
|
56
|
+
systems.each do |system|
|
57
|
+
included = system.include? file
|
58
|
+
break if included
|
59
|
+
end
|
60
|
+
|
61
|
+
included
|
62
|
+
end
|
63
|
+
|
64
|
+
# Compile all systems in this project.
|
65
|
+
def compile
|
66
|
+
compiler_results = []
|
67
|
+
@systems.each {|system| compiler_results << (system.compile).first}
|
68
|
+
compiler_results
|
69
|
+
end
|
70
|
+
|
71
|
+
# @return [REXML::Element] an XML element representing this project.
|
72
|
+
def to_xml_element
|
73
|
+
REXML::Element.new('Project').tap do |project|
|
74
|
+
project.add_element('Identifier').tap { |e| e.text = name }
|
75
|
+
project.add_element('Designer').tap { |e| e.text = designer }
|
76
|
+
project.add_element('DealerID').tap { |e| e.text = dealer }
|
77
|
+
project.add_element('SalesOrder').tap { |e| e.text = sales_order }
|
78
|
+
project.add_element('PurchaseOrder').tap { |e| e.text = purchase_order }
|
79
|
+
project.add_element('Comments').tap { |e| e.text = description }
|
80
|
+
|
81
|
+
@systems.each { |system| project << system.to_xml_element }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def parse_xml_element project
|
88
|
+
# Load project params.
|
89
|
+
@name = project.elements['Identifier'].text.strip || ''
|
90
|
+
@designer = project.elements['Designer'].text || ''
|
91
|
+
@dealer = project.elements['DealerID'].text || ''
|
92
|
+
@sales_order = project.elements['SalesOrder'].text || ''
|
93
|
+
@purchase_order = project.elements['PurchaseOrder'].text || ''
|
94
|
+
@description = project.elements['Comments'].text || ''
|
95
|
+
|
96
|
+
# Load systems.
|
97
|
+
project.each_element 'System' do |e|
|
98
|
+
system = NetLinx::System.new \
|
99
|
+
element: e,
|
100
|
+
project: self
|
101
|
+
|
102
|
+
@systems << system
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,226 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
require_relative 'system_file'
|
3
|
+
|
4
|
+
module NetLinx
|
5
|
+
# A collection of resources loaded onto a NetLinx master.
|
6
|
+
# Workspace -> Project -> System
|
7
|
+
class System
|
8
|
+
# A reference to the system's parent project.
|
9
|
+
attr_accessor :project
|
10
|
+
attr_accessor :files
|
11
|
+
|
12
|
+
attr_accessor :name
|
13
|
+
attr_accessor :active
|
14
|
+
attr_accessor :id
|
15
|
+
attr_accessor :description
|
16
|
+
|
17
|
+
attr_accessor :ip_address
|
18
|
+
attr_accessor :ip_port
|
19
|
+
attr_accessor :ensure_availability
|
20
|
+
|
21
|
+
attr_accessor :com_port
|
22
|
+
attr_accessor :baud_rate
|
23
|
+
attr_accessor :data_bits
|
24
|
+
attr_accessor :parity
|
25
|
+
attr_accessor :stop_bits
|
26
|
+
attr_accessor :flow_control
|
27
|
+
|
28
|
+
# @option kwargs [NetLinx::Project] :project This system's parent project node.
|
29
|
+
# @option kwargs [String] :name ('') System name.
|
30
|
+
# @option kwargs [String] :description ('')
|
31
|
+
# @option kwargs [Boolean] :active (false) True if this is the active system
|
32
|
+
# in the workspace.
|
33
|
+
# @option kwargs [Integer] :id (0) Master controller system ID.
|
34
|
+
# 0 connects to any master at the given communication settings.
|
35
|
+
# Or in other words, 0 prevents disconnection from a master
|
36
|
+
# with a different ID.
|
37
|
+
#
|
38
|
+
# @option kwargs [String] :ip_address ('0.0.0.0')
|
39
|
+
# @option kwargs [String] :ip_port (1319) ICSLan port.
|
40
|
+
# @option kwargs [String] :ensure_availability (true) Ping the master
|
41
|
+
# controller to ensure availability before connecting.
|
42
|
+
#
|
43
|
+
# @option kwargs [Symbol] :com_port (:com1)
|
44
|
+
# @option kwargs [Integer] :baud_rate (38400)
|
45
|
+
# @option kwargs [Integer] :data_bits (8)
|
46
|
+
# @option kwargs [:none,:even,:odd,:mark,:space] :parity (:none)
|
47
|
+
# @option kwargs [Integer] :stop_bits (1)
|
48
|
+
# @option kwargs [:none] :flow_control (:none)
|
49
|
+
def initialize **kwargs
|
50
|
+
@project = kwargs.fetch :project, nil
|
51
|
+
|
52
|
+
@name = kwargs.fetch :name, ''
|
53
|
+
@id = kwargs.fetch :id, 0
|
54
|
+
@active = kwargs.fetch :active, false
|
55
|
+
@description = kwargs.fetch :description, ''
|
56
|
+
|
57
|
+
@ip_address = kwargs.fetch :ip_address, '0.0.0.0'
|
58
|
+
@ip_port = kwargs.fetch :ip_port, 1319
|
59
|
+
@ensure_availability = kwargs.fetch :ensure_availability, true
|
60
|
+
|
61
|
+
@com_port = kwargs.fetch :com_port, :com1
|
62
|
+
@baud_rate = kwargs.fetch :baud_rate, 38400
|
63
|
+
@data_bits = kwargs.fetch :data_bits, 8
|
64
|
+
@parity = kwargs.fetch :parity, :none
|
65
|
+
@stop_bits = kwargs.fetch :stop_bits, 1
|
66
|
+
@flow_control = kwargs.fetch :flow_control, :none
|
67
|
+
|
68
|
+
@files = []
|
69
|
+
|
70
|
+
@compiler_target_files = []
|
71
|
+
@compiler_include_paths = []
|
72
|
+
@compiler_module_paths = []
|
73
|
+
@compiler_library_paths = []
|
74
|
+
|
75
|
+
system_element = kwargs.fetch :element, nil
|
76
|
+
parse_xml_element system_element if system_element
|
77
|
+
end
|
78
|
+
|
79
|
+
# Alias to add a file.
|
80
|
+
def << file
|
81
|
+
@files << file
|
82
|
+
file.system = self
|
83
|
+
end
|
84
|
+
|
85
|
+
# @return the system name.
|
86
|
+
def to_s
|
87
|
+
@name
|
88
|
+
end
|
89
|
+
|
90
|
+
# @return [REXML::Element] an XML element representing this system.
|
91
|
+
def to_xml_element
|
92
|
+
REXML::Element.new('System').tap do |system|
|
93
|
+
system.attributes['IsActive'] = active
|
94
|
+
system.attributes['Platform'] = 'Netlinx'
|
95
|
+
system.attributes['Transport'] = 'Serial'
|
96
|
+
system.attributes['TransportEx'] =
|
97
|
+
(ip_address == '0.0.0.0') ? 'Serial' : 'TCPIP'
|
98
|
+
|
99
|
+
system.add_element('Identifier').tap { |e| e.text = name }
|
100
|
+
system.add_element('SysID').tap { |e| e.text = id }
|
101
|
+
system.add_element('Comments').tap { |e| e.text = description }
|
102
|
+
|
103
|
+
# These don't seem to change in NetLinx Studio 4.0; possibly 3.x legacy.
|
104
|
+
# The 'Ex' suffixes are used.
|
105
|
+
system.add_element('TransTCPIP').tap { |e| e.text = '0.0.0.0' }
|
106
|
+
system.add_element('TransSerial').tap { |e| e.text = 'COM1,38400,8,None,1,None' }
|
107
|
+
|
108
|
+
# TODO: Generate communication settings.
|
109
|
+
system.add_element('TransTCPIPEx').tap { |e|
|
110
|
+
e.text = "#{ip_address}|#{ip_port}|#{ensure_availability ? 1 : 0}|||"
|
111
|
+
}
|
112
|
+
system.add_element('TransSerialEx').tap { |e|
|
113
|
+
e.text = "#{com_port.upcase}|#{baud_rate}|#{data_bits}|#{parity.capitalize}|#{stop_bits}|||"
|
114
|
+
}
|
115
|
+
system.add_element('TransUSBEx').tap { |e| e.text = '|||||' }
|
116
|
+
system.add_element('TransVNMEx').tap { |e| e.text = '||' }
|
117
|
+
|
118
|
+
system.add_element('UserName').tap { |e| e.text = '' }
|
119
|
+
system.add_element('Password').tap { |e| e.text = '' }
|
120
|
+
|
121
|
+
@files.each { |file| system << file.to_xml_element }
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# @see Test::NetLinx::Compilable.
|
126
|
+
def compiler_target_files
|
127
|
+
@files
|
128
|
+
.select {|f| f.type == :master}
|
129
|
+
.map {|f| File.expand_path \
|
130
|
+
f.path.gsub('\\', '/'),
|
131
|
+
f.system.project.workspace.path
|
132
|
+
}.uniq
|
133
|
+
end
|
134
|
+
|
135
|
+
# @see Test::NetLinx::Compilable.
|
136
|
+
def compiler_include_paths
|
137
|
+
@files
|
138
|
+
.select {|f| f.type == :include}
|
139
|
+
.map {|f| File.expand_path \
|
140
|
+
File.dirname(f.path.gsub('\\', '/')),
|
141
|
+
f.system.project.workspace.path
|
142
|
+
}.uniq
|
143
|
+
end
|
144
|
+
|
145
|
+
# @see Test::NetLinx::Compilable.
|
146
|
+
def compiler_module_paths
|
147
|
+
@files
|
148
|
+
.select {|f| f.type == :module || f.type == :tko || f.type == :duet}
|
149
|
+
.map {|f| File.expand_path \
|
150
|
+
File.dirname(f.path.gsub('\\', '/')),
|
151
|
+
f.system.project.workspace.path
|
152
|
+
}.uniq
|
153
|
+
end
|
154
|
+
|
155
|
+
# @see Test::NetLinx::Compilable.
|
156
|
+
def compiler_library_paths
|
157
|
+
[]
|
158
|
+
end
|
159
|
+
|
160
|
+
# @return [Boolean] true if the project contains the specified file.
|
161
|
+
def include? file
|
162
|
+
included = false
|
163
|
+
|
164
|
+
@files.each do |f|
|
165
|
+
name_included = f.name.downcase.eql? file.downcase
|
166
|
+
|
167
|
+
# TODO: This should probably be relative to the workspace path,
|
168
|
+
# which can be found by traversing @project, @workspace.
|
169
|
+
path_included = file.gsub(/\\/, '/').include? f.path.gsub(/\\/, '/')
|
170
|
+
|
171
|
+
included = name_included || path_included
|
172
|
+
break if included
|
173
|
+
end
|
174
|
+
|
175
|
+
included
|
176
|
+
end
|
177
|
+
|
178
|
+
# Compile this system.
|
179
|
+
def compile
|
180
|
+
# The compiler dependency is only needed if this method is called.
|
181
|
+
require 'netlinx/compiler'
|
182
|
+
|
183
|
+
compiler = NetLinx::Compiler.new
|
184
|
+
compiler.compile self
|
185
|
+
end
|
186
|
+
|
187
|
+
private
|
188
|
+
|
189
|
+
def parse_xml_element system
|
190
|
+
# Load system params.
|
191
|
+
@name = system.elements['Identifier'].text.strip || ''
|
192
|
+
@active = (system.attributes['IsActive'].strip == 'true') ? true : false
|
193
|
+
@id = system.elements['SysID'].text.strip.to_i || 0
|
194
|
+
@description = system.elements['Comments'].text || ''
|
195
|
+
|
196
|
+
if system.elements['TransTCPIPEx'] # Workspace v4.0
|
197
|
+
tcpip = (system.elements['TransTCPIPEx'].text || '').split('|')
|
198
|
+
|
199
|
+
@ip_address = tcpip[0] || '0.0.0.0'
|
200
|
+
@ip_port = (tcpip[1] || 1319).to_i
|
201
|
+
@ensure_availability = (tcpip[2] == '0') ? false : true
|
202
|
+
end
|
203
|
+
|
204
|
+
if system.elements['TransSerialEx'] # Workspace v4.0
|
205
|
+
serial = (system.elements['TransSerialEx'].text || '').split('|')
|
206
|
+
|
207
|
+
@com_port = (serial[0] || :com1).to_s.downcase.to_sym
|
208
|
+
@baud_rate = (serial[1] || 38400).to_i
|
209
|
+
@data_bits = (serial[2] || 8).to_i
|
210
|
+
@parity = (serial[3] || :none).to_s.downcase.to_sym
|
211
|
+
@stop_bits = (serial[4] || 1).to_i
|
212
|
+
@flow_control = (serial[5] || :none).to_s.downcase.to_sym
|
213
|
+
end
|
214
|
+
|
215
|
+
# Create system files.
|
216
|
+
system.each_element 'File' do |e|
|
217
|
+
system_file = NetLinx::SystemFile.new \
|
218
|
+
element: e,
|
219
|
+
system: self
|
220
|
+
|
221
|
+
@files << system_file
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
end
|
226
|
+
end
|