tallakt-plcutil 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,187 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'yaml'
4
+
5
+ module PlcUtil
6
+ # Reads a Siemens Step 7 AWL file and creates single tags with addresses and comment
7
+ class IntouchFile
8
+ ApostropheReqiredColumns =
9
+ %w(
10
+ SymbolicName Comment OnMsg Group AccessName HiAlarmInhibitor MajDevAlarmInhibitor DSCAlarmInhibitor InitialMessage
11
+ HiHiAlarmInhibitor tag Application MinDevAlarmInhibitor Topic AlarmComment EngUnits LoAlarmInhibitor
12
+ OffMsg LoLoAlarmInhibitor RocAlarmInhibitor ItemName
13
+ )
14
+
15
+ def initialize(filename = nil, options = {})
16
+ # Lookup table to check wether a certain column must write its values inside apostrophes
17
+ @lookup_apostrophe_fields = ApostropheReqiredColumns.inject({}) {|h, v| h[v] = true; h}
18
+
19
+ # load standard sections
20
+ @sections = YAML.load_file File.join(File.dirname(__FILE__), "standard_sections.yaml")
21
+ each_section {|sec| define_data_class @sections[sec][:colnames]}
22
+
23
+ @options = options
24
+
25
+ if filename
26
+ File.open filename, 'r' do |f|
27
+ read_csv f
28
+ end
29
+ end
30
+ end
31
+
32
+ def each_section
33
+ @sections.each_key {|k| yield k}
34
+ end
35
+
36
+
37
+ def find_tag(tag, section = nil)
38
+ if section
39
+ @sections[section][:rows].find {|row| row.tag == tag}
40
+ else
41
+ tmp = @sections.collect do |sec|
42
+ sec[:rows].find {|row| row.tag == tag }
43
+ end.flatten
44
+ tmp && tmp.first
45
+ end
46
+ end
47
+
48
+
49
+ def each_tag(section)
50
+ @sections[section][:rows].each {|r| yield r }
51
+ end
52
+
53
+ def clear
54
+ each_section do |s|
55
+ @sections[s][:rows] = []
56
+ end
57
+ end
58
+
59
+ def read_csv_file(filename)
60
+ File.open filename do |f|
61
+ read_csv f
62
+ end
63
+ end
64
+
65
+ def write_csv_file(filename)
66
+ File.open filename, 'w' do |f|
67
+ write_csv f
68
+ end
69
+ end
70
+
71
+
72
+ def read_csv(io)
73
+ colnames = nil
74
+ current = nil
75
+ io.each do |l|
76
+ case l.chomp!
77
+ when /^:mode=/
78
+ # ignore line
79
+ when /^:/
80
+ # new section
81
+ colnames = l.split /;/
82
+ name = colnames.first
83
+ unless @sections.key? name
84
+ @sections[name] = {}
85
+ @sections[name][:colnames] = colnames
86
+ @sections[name][:rows] = []
87
+ @sections[name][:name] = name
88
+ define_data_class colnames
89
+ end
90
+ current = @sections[name]
91
+ else
92
+ # new tag
93
+ cols = l.gsub(/"/, '').split /;/
94
+ new_data_instance(current[:name][1..-1], cols[0], cols[1, -1])
95
+ end
96
+ end
97
+ end
98
+
99
+ def write_csv(io, mode = :update)
100
+ throw 'Please use mode :ask/:update/:replace' unless [:ask, :replace, :update].include? mode
101
+ io.puts ':mode=' + mode.to_s
102
+ @sections.each_value do |section|
103
+ next if section[:rows].empty?
104
+ io.puts section[:colnames].join ';'
105
+ section[:rows].each do |row|
106
+ io.puts row.to_csv
107
+ end
108
+ end
109
+ end
110
+
111
+ private
112
+
113
+
114
+ def apostrophe_req?(colname)
115
+ @lookup_apostrophe_fields[colname] || colname.match(/^:/)
116
+ end
117
+
118
+ def to_csv_line(colnames)
119
+ '[%s].join ";"' % colnames.map do |c|
120
+ cc = c.match(/^:/) ? 'tag' : camel_conv(c)
121
+ if apostrophe_req? c
122
+ # When nil, dont display the "" chars, since this will overwrite with an empty string
123
+ # The check is performed at runtime in the generated class
124
+ # The result should look like: (tag ? '"%s"' : '%s') % tag
125
+ %Q!\n (#{cc} ? '"%s"' : '%s') % #{cc}!
126
+ else
127
+ %Q!\n #{cc}.to_s!
128
+ end
129
+ end.join(',')
130
+ end
131
+
132
+ def define_data_class(colnames)
133
+ # define new class for the data type
134
+ klass = colnames[0][1..-1]
135
+ #return if defined? klass
136
+ attrs = colnames[1..-1].map {|x| ':' + camel_conv(x) }.join(', ')
137
+ attr_list = colnames[1..-1].map {|x| camel_conv(x)}.join(', ')
138
+ values_format = colnames.map {|c| apostrophe_req?(c) ? '"%s"' : '%s'}.join(';')
139
+
140
+ PlcUtil.module_eval <<-END
141
+ class #{klass}
142
+ attr_accessor #{attrs}
143
+ attr_reader :tag
144
+
145
+ def initialize(tag, values=nil)
146
+ @tag = tag
147
+ #{attr_list} = values if values
148
+ end
149
+
150
+ def to_csv
151
+ #{to_csv_line colnames}
152
+ end
153
+
154
+ def intouch_fields
155
+ [#{attrs}]
156
+ end
157
+ end
158
+ END
159
+
160
+ # define method to create new instances
161
+ IntouchFile.class_eval <<-END
162
+ public
163
+
164
+ def new_#{camel_conv klass}(tag, values = nil)
165
+ result = #{klass}.new tag, values
166
+ @sections['#{colnames[0]}'][:rows] << result
167
+ if result.respond_to? :access_name
168
+ result.access_name = @options[:access_name]
169
+ end
170
+ yield result if block_given?
171
+ result
172
+ end
173
+ END
174
+ end
175
+
176
+ def new_data_instance(klass, tag, values)
177
+ method('new_' + camel_conv(klass)).call(tag, values)
178
+ end
179
+
180
+ def camel_conv(s)
181
+ res = s.gsub(/[A-Z]{3,}/){|x| x[0..-2].capitalize + x[-1..-1]}
182
+ res.gsub!(/[A-Z]/, '_\0')
183
+ res.gsub! /^_/ , ''
184
+ res.downcase!
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,114 @@
1
+ require 'optparse'
2
+ require 'plcutil/wonderware/intouchfile'
3
+
4
+ module PlcUtil
5
+ class IntouchReader
6
+ def initialize
7
+ # Standard options
8
+ @mode = :io
9
+
10
+ # Parse command line options
11
+ option_parser.parse! ARGV
12
+ if ARGV.size > 1
13
+ show_help
14
+ exit
15
+ end
16
+ filename, = ARGV
17
+
18
+
19
+
20
+ # Read from intouch file
21
+ @intouch_file = IntouchFile.new
22
+ if filename
23
+ File.open filename do |f|
24
+ @intouch_file.read_csv f
25
+ end
26
+ else
27
+ @intouch_file.read_csv $stdin
28
+ end
29
+
30
+ # print the tags in intouch
31
+ case @mode
32
+ when :io
33
+ print_io
34
+ when :alarm_groups
35
+ print_alarm_groups
36
+ when :access_names
37
+ print_access_names
38
+ when :tag
39
+ print_tag @tag
40
+ end
41
+ end
42
+
43
+ def print_io
44
+ ss = %w( :IODisc :IOReal :IndirectAnalog :MemoryReal :IOMsg :IndirectMsg
45
+ :MemoryMsg :MemoryDisc :IndirectDisc :IOInt :MemoryInt)
46
+ ss.each do |section|
47
+ @intouch_file.each_tag section do |row|
48
+ puts '%s%-33s%-20s%-20ss%s' % [
49
+ (row.respond_to?(:alarm_state) && row.alarm_state && row.alarm_state.match(/None/)) ? ' ' : '*',
50
+ row.tag,
51
+ row.respond_to?(:item_name) ? row.item_name : '',
52
+ row.access_name,
53
+ row.comment
54
+ ]
55
+ end
56
+ end
57
+ end
58
+
59
+
60
+ def print_alarm_groups
61
+ print_four_column_tags ':AlarmGroup'
62
+ end
63
+
64
+ def print_access_names
65
+ print_four_column_tags ':IOAccess'
66
+ end
67
+
68
+ def print_four_column_tags(section)
69
+ lines = []
70
+ @intouch_file.each_tag section do |tag|
71
+ lines << tag.tag
72
+ end
73
+ lines.each_slice(4) do |slice|
74
+ slice[3] ||= nil
75
+ puts ('%-20s' * 4) % slice
76
+ end
77
+ end
78
+
79
+ def print_tag(tag)
80
+ puts 'Tag: ' + tag
81
+ @intouch.find_tag(tag).intouch_fields.collect do |field|
82
+ [field.to_s, row.method(field).call]
83
+ end.each do |pair|
84
+ puts '%-20s%s' % pair
85
+ end
86
+ end
87
+
88
+ def option_parser
89
+ OptionParser.new do |opts|
90
+ opts.banner = "Usage: intouchreader [options] [DBFILE]"
91
+ opts.on("-c", "--access-names", "Show access name") do
92
+ @mode = :access_names
93
+ end
94
+ opts.on("-t", "--tag TAGNAME", "Show all fields for the specified tag") do |tag|
95
+ @mode = :tag
96
+ @tag = tag
97
+ end
98
+ opts.on("-a", "--alarm-groups", "Show alarm groups") do
99
+ @mode = :alarm_groups
100
+ end
101
+ opts.on_tail("-h", "--help", "Show this message") do
102
+ puts opts
103
+ exit
104
+ end
105
+ end
106
+ end
107
+
108
+ def show_help
109
+ puts option_parser
110
+ end end
111
+
112
+ IntouchReader.new
113
+ end
114
+