tallakt-plcutil 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/Manifest.txt +21 -0
- data/README.rdoc +61 -0
- data/Rakefile +27 -0
- data/TODO +4 -0
- data/bin/awlpp +8 -0
- data/bin/s7tointouch +7 -0
- data/lib/plcutil/siemens/awlfile.rb +290 -0
- data/lib/plcutil/siemens/awlprettyprintrunner.rb +73 -0
- data/lib/plcutil/siemens/step7tointouchrunner.rb +169 -0
- data/lib/plcutil/wonderware/intouchfile.rb +187 -0
- data/lib/plcutil/wonderware/intouchreader.rb +114 -0
- data/lib/plcutil/wonderware/standard_sections.yaml +442 -0
- data/lib/plcutil.rb +6 -0
- data/script/console +10 -0
- data/script/console.cmd +1 -0
- data/script/destroy +14 -0
- data/script/destroy.cmd +1 -0
- data/script/generate +14 -0
- data/script/generate.cmd +1 -0
- metadata +95 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
History.txt
|
2
|
+
LICENSE
|
3
|
+
Manifest.txt
|
4
|
+
README.rdoc
|
5
|
+
Rakefile
|
6
|
+
TODO
|
7
|
+
bin/awlpp
|
8
|
+
bin/s7tointouch
|
9
|
+
lib/plcutil/siemens/awlfile.rb
|
10
|
+
lib/plcutil/siemens/awlprettyprintrunner.rb
|
11
|
+
lib/plcutil/siemens/step7tointouchrunner.rb
|
12
|
+
lib/plcutil/wonderware/intouchfile.rb
|
13
|
+
lib/plcutil/wonderware/intouchreader.rb
|
14
|
+
lib/plcutil/wonderware/standard_sections.yaml
|
15
|
+
lib/plcutil.rb
|
16
|
+
script/console
|
17
|
+
script/console.cmd
|
18
|
+
script/destroy
|
19
|
+
script/destroy.cmd
|
20
|
+
script/generate
|
21
|
+
script/generate.cmd
|
data/README.rdoc
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
= plcutil
|
2
|
+
|
3
|
+
http://github.com/tallakt/plcutil
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
A set of command line utilities and helper classes to convert between IO list files of different PLC types into different HMI types.
|
8
|
+
|
9
|
+
Currently Siemens and Wonderware are supported, Schneider and Wonderware IAS support planned in the near future.
|
10
|
+
|
11
|
+
This is still a quite rough version - pre alpha!!
|
12
|
+
|
13
|
+
== FEATURES/PROBLEMS:
|
14
|
+
|
15
|
+
|
16
|
+
== SYNOPSIS:
|
17
|
+
|
18
|
+
Print the contents of Siemens Step 7 IO file
|
19
|
+
|
20
|
+
awlpp -s SYMLIST.DBF 00000007.AWL
|
21
|
+
|
22
|
+
Convert a siemens IO list into a Wonderware Intouch (non IAS) DB import file
|
23
|
+
|
24
|
+
s7tointouch -s SYMLIST.DBF -a SIEMENSPLC 00000007.AWL
|
25
|
+
|
26
|
+
The commands support the --help parameter for a full list of parameters
|
27
|
+
|
28
|
+
|
29
|
+
== REQUIREMENTS:
|
30
|
+
|
31
|
+
* FIX (list of requirements)
|
32
|
+
|
33
|
+
== INSTALL:
|
34
|
+
|
35
|
+
gem install plcutil
|
36
|
+
|
37
|
+
|
38
|
+
== LICENSE:
|
39
|
+
|
40
|
+
(The MIT License)
|
41
|
+
|
42
|
+
Copyright (c) 2009 Tallak Tveide
|
43
|
+
|
44
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
45
|
+
a copy of this software and associated documentation files (the
|
46
|
+
'Software'), to deal in the Software without restriction, including
|
47
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
48
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
49
|
+
permit persons to whom the Software is furnished to do so, subject to
|
50
|
+
the following conditions:
|
51
|
+
|
52
|
+
The above copyright notice and this permission notice shall be
|
53
|
+
included in all copies or substantial portions of the Software.
|
54
|
+
|
55
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
56
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
57
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
58
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
59
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
60
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
61
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
%w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
|
2
|
+
require File.dirname(__FILE__) + '/lib/plcutil'
|
3
|
+
|
4
|
+
# Generate all the Rake tasks
|
5
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
6
|
+
$hoe = Hoe.new('plcutil', Plcutil::VERSION) do |p|
|
7
|
+
p.developer('Tallak Tveide', 'tallak@tveide.net')
|
8
|
+
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
9
|
+
p.rubyforge_name = p.name # TODO this is default value
|
10
|
+
# p.extra_deps = [
|
11
|
+
# ['activesupport','>= 2.0.2'],
|
12
|
+
# ]
|
13
|
+
p.extra_dev_deps = [
|
14
|
+
['newgem', ">= #{::Newgem::VERSION}"]
|
15
|
+
]
|
16
|
+
|
17
|
+
p.clean_globs |= %w[**/.DS_Store tmp *.log]
|
18
|
+
path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
|
19
|
+
p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
|
20
|
+
p.rsync_args = '-av --delete --ignore-errors'
|
21
|
+
end
|
22
|
+
|
23
|
+
require 'newgem/tasks' # load /tasks/*.rake
|
24
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
25
|
+
|
26
|
+
# TODO - want other tests/tasks run by default? Add them to the list
|
27
|
+
# task :default => [:spec, :features]
|
data/TODO
ADDED
data/bin/awlpp
ADDED
data/bin/s7tointouch
ADDED
@@ -0,0 +1,290 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'dbf'
|
5
|
+
|
6
|
+
module PlcUtil
|
7
|
+
|
8
|
+
# Reads a Siemens Step 7 AWL file and creates single tags with addresses and comment
|
9
|
+
class AwlFile
|
10
|
+
def initialize(filename, options = {})
|
11
|
+
@types = {}
|
12
|
+
init_basic_types
|
13
|
+
@datablocks = []
|
14
|
+
@symlist = {}
|
15
|
+
|
16
|
+
if options[:symlist]
|
17
|
+
throw 'Specified symlist file not found' unless File.exists? options[:symlist]
|
18
|
+
table = DBF::Table.new(options[:symlist])
|
19
|
+
table.records.each do |rec|
|
20
|
+
@symlist[rec.attributes['_skz']] = rec.attributes['_opiec'] # or _ophist or _datatyp
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
@symlist.merge!(options[:blocks] || {}) # User may override blocks
|
25
|
+
|
26
|
+
File.open filename do |f|
|
27
|
+
parse f
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def each_tag
|
32
|
+
@datablocks.each do |var|
|
33
|
+
var.type.explode(Address.new(0,0), var.name).each do |item|
|
34
|
+
yield item[:name],
|
35
|
+
complete_address(var.name, item[:addr].to_s),
|
36
|
+
item[:comment],
|
37
|
+
item[:struct_comment],
|
38
|
+
item[:type].downcase.to_sym
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def data_block_address(name)
|
46
|
+
if @symlist.key? name
|
47
|
+
@symlist[name].gsub /\s+/, ''
|
48
|
+
else
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def complete_address(data_block_name, address)
|
54
|
+
[data_block_address(data_block_name), address].select {|s| s}.join ','
|
55
|
+
end
|
56
|
+
|
57
|
+
def init_basic_types
|
58
|
+
[BoolType.new, BasicType.new('INT', 2), BasicType.new('REAL', 4)].each do |basic|
|
59
|
+
add_type basic
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def add_type(type)
|
64
|
+
@types[type.name] = type
|
65
|
+
end
|
66
|
+
|
67
|
+
def lookup_type(type)
|
68
|
+
throw "Could not find type '#{type}'" unless @types.key? type
|
69
|
+
@types[type]
|
70
|
+
end
|
71
|
+
|
72
|
+
def parse(file)
|
73
|
+
stack = []
|
74
|
+
in_array_decl = false
|
75
|
+
tagname = start = stop = type = comment = nil
|
76
|
+
file.each_line do |l|
|
77
|
+
l.chomp!
|
78
|
+
if in_array_decl
|
79
|
+
if l.match '^\s+\"?([A-Za-z0-9_]+)"?\s?;'
|
80
|
+
type = $1
|
81
|
+
stack.last.add Variable.new(tagname, ArrayType.new(@types[type], start..stop), comment)
|
82
|
+
end
|
83
|
+
in_array_decl = false
|
84
|
+
else
|
85
|
+
in_array_decl = false
|
86
|
+
case l
|
87
|
+
when /^TYPE "([^"]+)/
|
88
|
+
stack = [StructType.new $1, :datatype]
|
89
|
+
add_type stack.last
|
90
|
+
when /^DATA_BLOCK "([^"]+)/
|
91
|
+
stack = [StructType.new $1, :datablock]
|
92
|
+
@datablocks << Variable.new($1, stack.last)
|
93
|
+
when /^\s*(\S+) : STRUCT /
|
94
|
+
s = StructType.new 'STRUCT', :anonymous
|
95
|
+
stack.last.add Variable.new $1, s
|
96
|
+
stack << stack.last.children.last.type
|
97
|
+
when /^\s+END_STRUCT/
|
98
|
+
stack.pop
|
99
|
+
when /^\s+([A-Za-z0-9_]+) : "?([A-Za-z0-9_]+)"?\s*(:=\s*[0-9.e+-]+)?;(\s*\/\/(.*))?/
|
100
|
+
# New variable in struct or data block
|
101
|
+
tagname, type_name, comment = $1, $2, $5
|
102
|
+
stack.last.add Variable.new(tagname, lookup_type(type_name), comment)
|
103
|
+
when /^\s+([A-Za-z0-9_]+) : ARRAY\s*\[(\d+)\D+(\d+) \] OF "?([A-Za-z0-9_]+)"?\s?;(\s*\/\/(.*))?/
|
104
|
+
tagname, start, stop, type, comment = $1, $2, $3, $4, $6
|
105
|
+
stack.last.add Variable.new(tagname, ArrayType.new(lookup_type(type), start..stop), comment)
|
106
|
+
when /^\s+([A-Za-z0-9_]+) : ARRAY\s*\[(\d+)\D+(\d+) \] OF(\s?\/\/(.*))?$/
|
107
|
+
tagname, start, stop, comment = $1, $2, $3, $4
|
108
|
+
in_array_decl = true
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def defined_type(type)
|
115
|
+
@types.key?(type.name)
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
class BasicType
|
120
|
+
attr_accessor :size, :name
|
121
|
+
|
122
|
+
def initialize(name, size)
|
123
|
+
@size, @name = size, name
|
124
|
+
end
|
125
|
+
|
126
|
+
def explode(start_addr, name, comment, struct_comment)
|
127
|
+
[:addr => start_addr.first_even_bit, :name => name,
|
128
|
+
:struct_comment => struct_comment, :comment => comment, :type => @name]
|
129
|
+
end
|
130
|
+
|
131
|
+
def end_address(start_address)
|
132
|
+
start_address.first_even_bit.skip!(size)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
|
137
|
+
class BoolType
|
138
|
+
attr_accessor :name
|
139
|
+
|
140
|
+
def initialize
|
141
|
+
@name = 'BOOL'
|
142
|
+
end
|
143
|
+
|
144
|
+
def explode(start_addr, name, comment, struct_comment)
|
145
|
+
[:addr => start_addr, :name => name,
|
146
|
+
:struct_comment => struct_comment, :comment => comment, :type => @name]
|
147
|
+
end
|
148
|
+
|
149
|
+
def end_address(start_address)
|
150
|
+
start_address.next_bit
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
class StructType
|
155
|
+
attr_accessor :name, :children, :type
|
156
|
+
|
157
|
+
def initialize(name = 'STRUCT', type = :anonymous)
|
158
|
+
@name = name
|
159
|
+
@children = []
|
160
|
+
@type = type
|
161
|
+
end
|
162
|
+
|
163
|
+
def add(child)
|
164
|
+
throw 'Added nil child' unless child
|
165
|
+
throw 'Added nil child type' unless child.type
|
166
|
+
@children << child
|
167
|
+
end
|
168
|
+
|
169
|
+
def end_address(start_address)
|
170
|
+
addr = start_address.first_even_bit
|
171
|
+
@children.each do |child|
|
172
|
+
addr = child.type.end_address addr
|
173
|
+
end
|
174
|
+
addr.first_bit!
|
175
|
+
end
|
176
|
+
|
177
|
+
def explode(start_addr, name, comment = nil, struct_comment = nil)
|
178
|
+
addr = start_addr.first_even_bit
|
179
|
+
exploded = []
|
180
|
+
@children.each do |child|
|
181
|
+
exploded += child.type.explode(addr, name + '.' + child.name, child.comment,
|
182
|
+
type == :datablock ? child.comment : struct_comment)
|
183
|
+
addr = child.type.end_address addr
|
184
|
+
end
|
185
|
+
exploded
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
|
190
|
+
class ArrayType
|
191
|
+
attr_accessor :range, :type
|
192
|
+
|
193
|
+
def initialize(type, range)
|
194
|
+
throw 'Added nil array type' unless type
|
195
|
+
throw 'Added nil array range' unless range
|
196
|
+
@range, @type = range, type
|
197
|
+
end
|
198
|
+
|
199
|
+
def name
|
200
|
+
'ARRAY'
|
201
|
+
end
|
202
|
+
|
203
|
+
def end_address(start_address)
|
204
|
+
addr = start_address
|
205
|
+
range.each do
|
206
|
+
addr = type.end_address addr
|
207
|
+
end
|
208
|
+
addr
|
209
|
+
end
|
210
|
+
|
211
|
+
def explode(start_addr, name, comment, struct_comment)
|
212
|
+
exploded = []
|
213
|
+
addr = start_addr
|
214
|
+
range.to_a.each_with_index do |v, i|
|
215
|
+
exploded += type.explode(addr, name + '[' + v.to_s + ']', comment, struct_comment)
|
216
|
+
addr = type.end_address(addr)
|
217
|
+
end
|
218
|
+
exploded
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
class Variable
|
223
|
+
attr_accessor :name, :type, :comment
|
224
|
+
|
225
|
+
def initialize(name, type, comment = nil)
|
226
|
+
@name, @type, @comment= name, type, comment
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
class Address
|
231
|
+
attr_accessor :byte, :bit
|
232
|
+
|
233
|
+
def initialize(byte, bit)
|
234
|
+
@byte, @bit = byte, bit
|
235
|
+
end
|
236
|
+
|
237
|
+
def to_s
|
238
|
+
@byte.to_s + '.' + @bit.to_s
|
239
|
+
end
|
240
|
+
|
241
|
+
def first_bit!
|
242
|
+
@byte += 1 if @bit > 0
|
243
|
+
@bit = 0
|
244
|
+
self
|
245
|
+
end
|
246
|
+
|
247
|
+
def first_bit
|
248
|
+
self.clone.first_bit!
|
249
|
+
end
|
250
|
+
|
251
|
+
def first_even_bit!
|
252
|
+
first_bit!
|
253
|
+
skip! if @byte % 2 > 0
|
254
|
+
self
|
255
|
+
end
|
256
|
+
|
257
|
+
def first_even_bit
|
258
|
+
self.clone.first_even_bit!
|
259
|
+
end
|
260
|
+
|
261
|
+
def next
|
262
|
+
self.clone.next!
|
263
|
+
end
|
264
|
+
|
265
|
+
def next_bit!
|
266
|
+
@bit += 1
|
267
|
+
if @bit == 8
|
268
|
+
@bit = 0
|
269
|
+
@byte += 1
|
270
|
+
end
|
271
|
+
self
|
272
|
+
end
|
273
|
+
|
274
|
+
def next_bit
|
275
|
+
self.clone.next_bit!
|
276
|
+
end
|
277
|
+
|
278
|
+
def skip!(bytes = 1)
|
279
|
+
(bytes * 8).times do
|
280
|
+
next_bit!
|
281
|
+
end
|
282
|
+
self
|
283
|
+
end
|
284
|
+
|
285
|
+
def skip(bytes = 1)
|
286
|
+
self.clone.skip bytes
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'plcutil/siemens/awlfile'
|
5
|
+
|
6
|
+
module PlcUtil
|
7
|
+
# Command line tool to read and output an awl file
|
8
|
+
class AwlPrettyPrintRunner
|
9
|
+
def initialize(args)
|
10
|
+
@awloptions = {}
|
11
|
+
@format = '%-11s %-40s%-10s%s'
|
12
|
+
@commentformat = '# %s / %s'
|
13
|
+
@output = nil
|
14
|
+
@symlistfile = nil
|
15
|
+
option_parser.parse! args
|
16
|
+
if args.size != 1
|
17
|
+
show_help
|
18
|
+
exit
|
19
|
+
end
|
20
|
+
filename, = args
|
21
|
+
@awl = AwlFile.new filename, @awloptions
|
22
|
+
if @output
|
23
|
+
File.open @output, 'w' do |f|
|
24
|
+
print_to_file f
|
25
|
+
end
|
26
|
+
else
|
27
|
+
print_to_file $stdout
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
def print_to_file(f)
|
33
|
+
@awl.each_tag do |name, addr, comment, struct_comment, type|
|
34
|
+
f.puts @format % [
|
35
|
+
addr,
|
36
|
+
name,
|
37
|
+
type.to_s,
|
38
|
+
[comment, struct_comment].compact! ? '' : (@commentformat % [comment, struct_comment])
|
39
|
+
]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def option_parser
|
44
|
+
OptionParser.new do |opts|
|
45
|
+
opts.banner = "Usage: awlpp [options] AWLFILE"
|
46
|
+
opts.on("-c", "--csv", String, "Output as CSV file") do
|
47
|
+
format = '%s;%s;%s;%s'
|
48
|
+
@commentformat = '%s'
|
49
|
+
end
|
50
|
+
opts.on("-s", "--symlist FILE", String, "Specify SYMLIST.DBF file from S7 project ") do |symlistfile|
|
51
|
+
@awloptions[:symlist] = symlistfile
|
52
|
+
end
|
53
|
+
opts.on("-b", "--block NAME=ADDR", String, "Define address of datablock without", "reading symlist") do |blockdef|
|
54
|
+
name, addr = blockdef.split(/=/)
|
55
|
+
@awloptions[:blocks] ||= {}
|
56
|
+
@awloptions[:blocks][name] = addr
|
57
|
+
end
|
58
|
+
opts.on("-o", "--output FILE", String, "Output to specified file instead of", "standard output") do |output|
|
59
|
+
@output = output
|
60
|
+
end
|
61
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
62
|
+
puts opts
|
63
|
+
exit
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def show_help
|
69
|
+
puts option_parser
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'plcutil/siemens/awlfile'
|
5
|
+
require 'plcutil/wonderware/intouchfile'
|
6
|
+
|
7
|
+
module PlcUtil
|
8
|
+
# Command line tool to read and output an awl file
|
9
|
+
class Step7ToIntouchRunner
|
10
|
+
def initialize(command_line_arguments)
|
11
|
+
# standard command line options
|
12
|
+
@awloptions = {}
|
13
|
+
@intouchoptions = {}
|
14
|
+
@output = nil
|
15
|
+
@symlistfile = nil
|
16
|
+
@no_block = false
|
17
|
+
@access_name = nil
|
18
|
+
@filter_file = nil
|
19
|
+
|
20
|
+
# Parse command line options
|
21
|
+
option_parser.parse! command_line_arguments
|
22
|
+
if command_line_arguments.empty?
|
23
|
+
show_help
|
24
|
+
exit
|
25
|
+
end
|
26
|
+
|
27
|
+
# Read Siemens S7 file
|
28
|
+
@awllist = command_line_arguments.map{|filename| AwlFile.new filename, @awloptions}
|
29
|
+
|
30
|
+
# create a lookup table for used tags in the file to prevent duplicate ids
|
31
|
+
@used_tags = {}
|
32
|
+
@awllist.each do |awl|
|
33
|
+
awl.each_tag do |tag, addr, comment, type|
|
34
|
+
@used_tags[siemens_to_ww_tagname_long tag] = true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# load filter file to enable override of functions filter_comment_format and filter_handle_tag
|
39
|
+
load @filter_file if @filter_file && File.exists?(@filter_file)
|
40
|
+
|
41
|
+
# Write to intouch file
|
42
|
+
if @output
|
43
|
+
File.open @output, 'w' do |f|
|
44
|
+
print_to_file f
|
45
|
+
end
|
46
|
+
else
|
47
|
+
print_to_file $stdout
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# This function may be overriden in filter ruby file
|
52
|
+
def filter_comment_format(comment, struct_comment)
|
53
|
+
if comment || struct_comment
|
54
|
+
[comment, struct_comment].compact.join(' / ').gsub(/"/, '')
|
55
|
+
else
|
56
|
+
''
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# This function may be overridden in filter ruby file
|
61
|
+
def filter_handle_tag(name, addr, comment, struct_comment, type, intouch_file)
|
62
|
+
ww_name = siemens_to_ww_tagname name
|
63
|
+
cc = filter_comment_format comment, struct_comment
|
64
|
+
case type
|
65
|
+
when :bool
|
66
|
+
intouch_file.new_io_disc(ww_name) do |io|
|
67
|
+
io.item_name = addr
|
68
|
+
io.comment = cc
|
69
|
+
end
|
70
|
+
when :int
|
71
|
+
intouch_file.new_io_int(ww_name) do |io|
|
72
|
+
io.item_name = addr
|
73
|
+
io.comment = cc
|
74
|
+
end
|
75
|
+
when :real
|
76
|
+
intouch_file.new_io_real(ww_name) do |io|
|
77
|
+
io.item_name = addr
|
78
|
+
io.comment = cc
|
79
|
+
end
|
80
|
+
else
|
81
|
+
throw RuntimeException.new 'Unsupported type found: ' + type.to_s
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
# This function may be overridden in filter ruby file
|
87
|
+
def filter_handle_awl_files
|
88
|
+
@awlfile.each do |awl|
|
89
|
+
awl.each_tag do |name, addr, comment, struct_comment, type|
|
90
|
+
filter_handle_tag name, addr, comment, struct_comment, type, @intouchfile
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
def print_to_file(f)
|
97
|
+
@intouchfile = IntouchFile.new nil, @intouchoptions
|
98
|
+
filter_handle_awl_files
|
99
|
+
i.write_csv f
|
100
|
+
end
|
101
|
+
|
102
|
+
def siemens_to_ww_tagname(s)
|
103
|
+
new_unique_tag(siemens_to_ww_tagname_long s)
|
104
|
+
end
|
105
|
+
|
106
|
+
def siemens_to_ww_tagname_long(s)
|
107
|
+
if @no_block
|
108
|
+
s.gsub /^[^\.]*./, ''
|
109
|
+
else
|
110
|
+
s
|
111
|
+
end.gsub(/\./, '_').gsub(/\[(\d+)\]/) { '_' + $1 }
|
112
|
+
end
|
113
|
+
|
114
|
+
def new_unique_tag_helper(s, n)
|
115
|
+
s[0..(31 - n.to_s.size - 1)] + '%' + n.to_s
|
116
|
+
end
|
117
|
+
|
118
|
+
def new_unique_tag(s)
|
119
|
+
if s.size < 33
|
120
|
+
s
|
121
|
+
else
|
122
|
+
n = nil
|
123
|
+
new_tag = new_unique_tag_helper s, n
|
124
|
+
while @used_tags.key? new_tag
|
125
|
+
n ||= 0
|
126
|
+
n += 1
|
127
|
+
new_tag = new_unique_tag_helper s, n
|
128
|
+
end
|
129
|
+
@used_tags[new_tag] = true
|
130
|
+
new_tag
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def option_parser
|
135
|
+
OptionParser.new do |opts|
|
136
|
+
opts.banner = "Usage: s7tointouch [options] AWLFILE [AWLFILE ...]"
|
137
|
+
opts.on("-s", "--symlist FILE", String, "Specify SYMLIST.DBF file from S7 project ") do |symlistfile|
|
138
|
+
@awloptions[:symlist] = symlistfile
|
139
|
+
end
|
140
|
+
opts.on("-n", "--no-block", String, "Dont use the datablock as part of the tag", "name") do
|
141
|
+
@no_block = true
|
142
|
+
end
|
143
|
+
opts.on("-b", "--block NAME=ADDR", String, "Define address of datablock without", "reading ymlist") do |blockdef|
|
144
|
+
name, addr = blockdef.split(/=/)
|
145
|
+
@awloptions[:blocks] ||= {}
|
146
|
+
@awloptions[:blocks][name] = addr
|
147
|
+
end
|
148
|
+
opts.on("-a", "--access ACCESSNAME", String, "Set access name for all tags") do |access_name|
|
149
|
+
@intouchoptions[:access_name] = access_name
|
150
|
+
end
|
151
|
+
opts.on("-f", "--filter FILTER_RUBY_FILE", String, "Specify ruby filter file to override", "filter functions") do |filter_file|
|
152
|
+
@filter_file = filter_file
|
153
|
+
end
|
154
|
+
opts.on("-o", "--output FILE", String, "Output to specified file instead of", "standard output") do |output|
|
155
|
+
@output = output
|
156
|
+
end
|
157
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
158
|
+
puts opts
|
159
|
+
exit
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def show_help
|
165
|
+
puts option_parser
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|