tallakt-plcutil 0.2.3 → 0.2.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/History.txt CHANGED
@@ -1,3 +1,9 @@
1
+ == 0.2.4 2012-07-13
2
+
3
+ * awlpp completely rewritten based on treetop parser
4
+ * restructuring of command line tools
5
+ * s7tointouch currently not working at all
6
+
1
7
  == 0.2.2 2011-11-11
2
8
 
3
9
  * clean up for Ruby 1.9 support
data/README.rdoc CHANGED
@@ -8,7 +8,8 @@ A set of command line utilities and helper classes to convert between IO list fi
8
8
 
9
9
  Currently Siemens, Schneider and Wonderware are supported, Schneider and Wonderware IAS support planned in the near future.
10
10
 
11
- This is still a quite rough version - pre alpha!!
11
+ The utilities in this gem are somewhat rough quality, no guarantees are given
12
+ wrt correct operation. Even so, I hope they might be useful.
12
13
 
13
14
  == FEATURES/PROBLEMS:
14
15
 
data/bin/awlpp CHANGED
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
- require 'rubygems'
3
- require 'tallakt-plcutil/siemens/awl_pretty_print_runner'
2
+ $:.unshift File.expand_path('../../lib', __FILE__)
3
+ require 'plcutil/siemens/awl_pretty_print_runner'
4
4
 
5
- # try: awlpp --help
6
- PlcUtil::AwlPrettyPrintRunner.new ARGV
5
+ PlcUtil::AwlPrettyPrintRunner.run
7
6
 
data/bin/intouchpp CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
- require 'rubygems'
3
- require 'tallakt-plcutil/wonderware/intouch_pretty_print_runner'
2
+ $:.unshift File.expand_path('../../lib', __FILE__)
3
+ require 'plcutil/wonderware/intouch_pretty_print_runner'
4
4
 
5
5
  # try: intouchpp --help
6
6
  PlcUtil::IntouchPrettyPrintRunner.new ARGV
data/bin/pl7tointouch CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
- require 'rubygems'
3
- require 'tallakt-plcutil/schneider/pl7_to_intouch_runner'
2
+ $:.unshift File.expand_path('../../lib', __FILE__)
3
+ require 'plcutil/schneider/pl7_to_intouch_runner'
4
4
 
5
5
  # try: pl7tointouch --help
6
6
  PlcUtil::PL7ToIntouchRunner.new ARGV
data/bin/s7tointouch CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
- require 'rubygems'
3
- require 'tallakt-plcutil/siemens/step7_to_intouch_runner'
2
+ $:.unshift File.expand_path('../../lib', __FILE__)
3
+ require 'plcutil/siemens/step7_to_intouch_runner'
4
4
 
5
5
  # try: s7tointouch --help
6
6
  PlcUtil::Step7ToIntouchRunner.new ARGV
@@ -2,7 +2,7 @@
2
2
  #encoding: utf-8
3
3
 
4
4
  require 'optparse'
5
- require 'tallakt-plcutil/wonderware/intouchfile'
5
+ require 'plcutil/wonderware/intouchfile'
6
6
  require 'ostruct'
7
7
 
8
8
  module PlcUtil
@@ -46,15 +46,10 @@ module PlcUtil
46
46
 
47
47
  def read_pl7_file(io)
48
48
  io.each_line do |l|
49
- mres = l.match(/^%M(W|F)?(\d+)(:X(\d+))?\t(\S+)\t(\S+)(\t\"?([^\t"]*))?/)
49
+ mres = l.match(/^%M(W)?(\d+)(:X(\d+))?\t(\S+)\t(\S+)(\t\"?([^\t"]*))?/)
50
50
  if mres
51
51
  item = OpenStruct.new
52
- case mres[1]
53
- when 'W'
54
- item.is_word = true
55
- when 'F'
56
- item.is_float = true
57
- end
52
+ item.is_word = mres[1]
58
53
  item.index = mres[2].to_i
59
54
  item.bit_index = mres[4] && mres[4].to_i
60
55
  item.tagname = mres[5]
@@ -88,10 +83,6 @@ module PlcUtil
88
83
  @intouchfile.new_io_int(tag, data) do |io|
89
84
  io.item_name = '%d S' % (item.index + 400001)
90
85
  end
91
- elsif item.is_float
92
- @intouchfile.new_io_real(tag, data) do |io|
93
- io.item_name = '%d F' % (item.index + 400001)
94
- end
95
86
  else
96
87
  @intouchfile.new_io_disc(tag, data) do |io|
97
88
  io.item_name = (item.index + 1).to_s
@@ -0,0 +1,21 @@
1
+ module PlcUtil
2
+ module Awl
3
+ class ArrayType
4
+ attr_accessor :range, :element_type
5
+
6
+ def initialize(element_type, range)
7
+ @range, @element_type = range, element_type
8
+ end
9
+
10
+ def skip_padding(addr)
11
+ addr.next_word
12
+ end
13
+
14
+ def end_address(start_addr)
15
+ start_addr.skip type.bit_size * range.count
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+
@@ -0,0 +1,172 @@
1
+ module PlcUtil
2
+ module Awl
3
+ grammar AwlGrammar
4
+ rule root
5
+ toplevel <TopLevelNode>
6
+ end
7
+
8
+ rule toplevel
9
+ (udt <UdtNode> / db <DbNode> / ob <ObNode>)*
10
+ end
11
+
12
+ rule db
13
+ 'DATA_BLOCK ' name eol
14
+ title
15
+ 'VERSION : ' version eol
16
+ eol*
17
+ root_struct_decl
18
+ 'BEGIN' eol
19
+ assignment*
20
+ 'END_DATA_BLOCK' eol
21
+ ws_eol
22
+ end
23
+
24
+ rule udt
25
+ 'TYPE ' name eol
26
+ 'VERSION : ' version eol
27
+ eol*
28
+ root_struct_decl
29
+ 'END_TYPE' eol
30
+ ws_eol
31
+ end
32
+
33
+ rule ob
34
+ 'ORGANIZATION_BLOCK ' name eol
35
+ title
36
+ 'VERSION : ' version eol
37
+ eol*
38
+ (!ob_end .)*
39
+ ob_end eol
40
+ ws_eol
41
+ end
42
+
43
+ rule name
44
+ quoted_string <QuotedNameNode> / db_name <DbNameNode>
45
+ end
46
+
47
+ rule quoted_string
48
+ '"' v:(!'"' .)* '"'
49
+ end
50
+
51
+ rule single_quoted_string
52
+ single_quote (!single_quote .)* single_quote
53
+ end
54
+
55
+ rule single_quote
56
+ "'"
57
+ end
58
+
59
+ rule db_name
60
+ 'DB ' number:([0-9]+)
61
+ end
62
+
63
+ rule version
64
+ [0-9]+ '.' [0-9]+
65
+ end
66
+
67
+ rule title
68
+ 'TITLE =' optional_title:(ws title_quoted:(quoted_string))? eol
69
+ end
70
+
71
+ rule ob_end
72
+ 'END_ORGANIZATION_BLOCK'
73
+ end
74
+
75
+ rule assignment
76
+ ws identifier ws ':=' ws value ';' ws eol
77
+ end
78
+
79
+ rule declaration
80
+ ws
81
+ identifier
82
+ ' : '
83
+ d:(array_declaration <ArrayDeclarationNode> / nonarray_declaration <NonArrayDeclarationNode>)
84
+ eol
85
+ end
86
+
87
+ rule nonarray_declaration
88
+ non_array_data_type
89
+ ws?
90
+ initial_value? ws?
91
+ ';'
92
+ ws? comment:(line_comment?)
93
+ end
94
+
95
+ rule initial_value
96
+ ':=' ws single_quoted_string
97
+ end
98
+
99
+ rule array_declaration
100
+ 'ARRAY [' ar_begin:([0-9]+) ' .. ' ar_end:([0-9]+) ' ] OF '
101
+ array_comment:(line_comment eol ws)? non_array_data_type ws ';' ws
102
+ end
103
+
104
+ rule non_array_data_type
105
+ basic_data_type <BasicDataTypeNode>
106
+ /
107
+ struct_data_type <StructDataTypeNode>
108
+ /
109
+ udt_data_type <UdtDataTypeNode>
110
+ end
111
+
112
+ rule struct_data_type
113
+ 'STRUCT' ws? eol
114
+ decl:declaration*
115
+ ws 'END_STRUCT'
116
+ end
117
+
118
+ rule root_struct_decl
119
+ ws struct_data_type ws ';' ws? eol
120
+ end
121
+
122
+ rule udt_data_type
123
+ quoted_string
124
+ end
125
+
126
+ rule identifier
127
+ non_ws+
128
+ end
129
+
130
+ rule line_comment
131
+ '//' comment_body:(!eol .)*
132
+ end
133
+
134
+ rule basic_data_type
135
+ (
136
+ 'INT' /
137
+ 'DINT' /
138
+ 'BOOL' /
139
+ 'BYTE' /
140
+ 'WORD' /
141
+ 'DWORD' /
142
+ 'TIME_OF_DAY' /
143
+ 'REAL' /
144
+ 'TIME' /
145
+ 'S5TIME' /
146
+ 'DATE' /
147
+ 'CHAR'
148
+ )
149
+ end
150
+
151
+ rule value
152
+ (single_quoted_string / (!(ws / ';') .)+)
153
+ end
154
+
155
+ rule eol
156
+ "\r"? "\n"
157
+ end
158
+
159
+ rule ws
160
+ (' ' / "\t")+
161
+ end
162
+
163
+ rule non_ws
164
+ (!ws .)
165
+ end
166
+
167
+ rule ws_eol
168
+ (ws / eol)+
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'polyglot'
4
+ require 'treetop'
5
+ require 'plcutil/siemens/awl/basic_type'
6
+ require 'plcutil/siemens/awl/struct_type'
7
+ require 'plcutil/siemens/awl/array_type'
8
+ require 'plcutil/siemens/awl/treetop_nodes'
9
+ require 'plcutil/siemens/awl/db_address'
10
+ require 'plcutil/siemens/awl/awl.treetop'
11
+
12
+
13
+ module PlcUtil
14
+ module Awl
15
+ class AwlFile
16
+ attr_reader :symlist
17
+
18
+ def initialize(filename, options = {})
19
+ @symlist = options[:symlist] && SymlistFile.new(options[:symlist])
20
+ # parse file
21
+ parser = PlcUtil::Awl::AwlGrammarParser.new
22
+ awl_nodes = parser.parse File.read(filename)
23
+ @awl = awl_nodes && awl_nodes.visit
24
+ if !@awl
25
+ raise [
26
+ "Unable to parse file: #{filename}",
27
+ "Failure on line #{parser.failure_line} column #{parser.failure.column}",
28
+ "Details:",
29
+ parser.failure_reason.inspect,
30
+ ].join("\n")
31
+ end
32
+ end
33
+
34
+ def lookup_symlist(tag)
35
+ (options[:blocks] && options[:blocks][tag]) || (@symlist && @symlist[tag])
36
+ end
37
+
38
+ def each_exploded(options = {}, &block)
39
+ @awl[:dbs].each do |raw|
40
+ db = create_struct raw
41
+ name = if options[:no_block]
42
+ ''
43
+ else
44
+ raw[:id]
45
+ end
46
+
47
+ a = DbAddress.new 0, db_address(raw[:name])
48
+ db.each_exploded(a, name) do |addr, name, comment, type_name|
49
+ yield addr, name, comment, type_name
50
+ end
51
+ end
52
+ end
53
+
54
+ def db_address(name)
55
+ n = @symlist[name]
56
+ n && n.gsub(/\s/, '')
57
+ end
58
+ private :db_address
59
+
60
+ def get_udt(name)
61
+ udt = @awl[:udts].find {|u| u[:name] == name }
62
+ raise "Could not find UDT with name: #{name}" unless udt
63
+ create_struct udt
64
+ end
65
+ private :get_udt
66
+
67
+ def create_struct(raw)
68
+ StructType.new.tap do |s|
69
+ raw[:entries].each do |e|
70
+ base_type = case e[:data_type]
71
+ when Hash
72
+ # anonymous structure inline
73
+ create_struct(e[:data_type])
74
+ when String
75
+ # UDT
76
+ get_udt e[:data_type]
77
+ when Symbol
78
+ BasicType::create e[:data_type]
79
+ end
80
+ if e.key? :array
81
+ s.add e[:id], ArrayType.new(base_type, e[:array]), e[:comment]
82
+ else
83
+ s.add e[:id], base_type, e[:comment]
84
+ end
85
+ end
86
+ end
87
+ end
88
+ private :create_struct
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,52 @@
1
+ module PlcUtil
2
+ module Awl
3
+ class BasicType
4
+ TYPES = {
5
+ :bool => 1,
6
+ :byte => 8,
7
+ :char => 8,
8
+ :date => 16,
9
+ :dint => 32,
10
+ :dword => 32,
11
+ :int => 16,
12
+ :real => 32,
13
+ :s5time => 16,
14
+ :time => 32,
15
+ :time_of_day => 32,
16
+ :word => 16,
17
+ :date_and_time => 32, # only in VAR_TEMP
18
+ :timer => 1, # only in FC calls
19
+ :cont_c => 125 * 8
20
+ }
21
+
22
+ attr_accessor :bit_size, :type_name
23
+
24
+ def initialize(bit_size, type_name)
25
+ @bit_size, @type_name = bit_size, type_name
26
+ end
27
+
28
+ def skip_padding(addr)
29
+ case bit_size
30
+ when 1
31
+ addr
32
+ when 8
33
+ addr.next_byte
34
+ else
35
+ addr.next_word
36
+ end
37
+ end
38
+
39
+ def end_address(start_addr)
40
+ start_addr.skip bit_size
41
+ end
42
+
43
+ def BasicType.create(type_key)
44
+ b = TYPES[type_key]
45
+ raise "Could not find type: #{type_key.to_s}" unless b
46
+ BasicType.new b, type_key
47
+ end
48
+ end
49
+
50
+ end
51
+ end
52
+
@@ -0,0 +1,52 @@
1
+ module PlcUtil
2
+ module Awl
3
+ class DbAddress
4
+ attr_accessor :data_block_addr
5
+
6
+ def initialize(bit_addr, data_block_addr = nil)
7
+ @bit_addr, @data_block_addr = bit_addr, data_block_addr
8
+ end
9
+
10
+ def to_s
11
+ (data_block_addr || 'DB???') + ',' + byte.to_s + '.' + bit.to_s
12
+ end
13
+
14
+ def bit
15
+ @bit_addr % 8
16
+ end
17
+
18
+ def byte
19
+ @bit_addr / 8
20
+ end
21
+
22
+ def next_word
23
+ # simatic packing rules
24
+ # 1. 16 bit values aligned at modulo 2 address 0.0, 2.0, 4.0, 8.0, ...
25
+ # 2. 8 bit values aligned at each address, 0.0, 1.0, 2.0, ...
26
+ # 3. Structs are aligned modulo 2
27
+ # 4. Bools are aligned like 8 bit values, consecutive bools occupy a single byte
28
+
29
+ DbAddress.new(next_n(16), data_block_addr)
30
+ end
31
+
32
+ def next_byte
33
+ DbAddress.new(next_n(8), data_block_addr)
34
+ end
35
+
36
+
37
+ def next_n(size)
38
+ m = @bit_addr % size
39
+ if m > 0
40
+ (@bit_addr / size + 1) * size
41
+ else
42
+ @bit_addr
43
+ end
44
+ end
45
+ private :next_n
46
+
47
+ def skip(bit_count)
48
+ DbAddress.new @bit_addr + bit_count, data_block_addr
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,52 @@
1
+ module PlcUtil
2
+ module Awl
3
+ class StructType
4
+ def initialize
5
+ @children = []
6
+ end
7
+
8
+ def add(name, type, comment)
9
+ @children << {:name => name, :type => type, :comment => comment}
10
+ end
11
+
12
+ def skip_padding(addr)
13
+ addr.next_word
14
+ end
15
+
16
+ def end_address(start_addr)
17
+ @children.inject(start_addr) {|addr, ch| ch[:type].end_address addr }
18
+ end
19
+
20
+ def each_exploded(start_addr, name_prefix, &block)
21
+ addr = skip_padding start_addr
22
+ @children.each do |ch|
23
+ full_name = if !name_prefix || name_prefix.empty?
24
+ ch[:name]
25
+ else
26
+ "#{name_prefix}.#{ch[:name]}"
27
+ end
28
+ case ch[:type]
29
+ when StructType
30
+ addr = ch[:type].each_exploded(addr, full_name) do |a, n, c, t|
31
+ yield a, n, c, t
32
+ end
33
+ when ArrayType
34
+ ct = ch[:type]
35
+ addr = ct.skip_padding addr
36
+ ct.range.each do |n|
37
+ yield addr, "#{full_name}[#{n}]", ch[:comment], ct.element_type.type_name
38
+ addr = ct.element_type.end_address addr
39
+ end
40
+ when BasicType
41
+ ct = ch[:type]
42
+ addr = ct.skip_padding addr
43
+ yield addr, full_name, ch[:comment], ct.type_name
44
+ addr = ct.end_address addr
45
+ end
46
+ end
47
+ addr
48
+ end
49
+ end
50
+ end
51
+ end
52
+
@@ -0,0 +1,28 @@
1
+ require 'dbf'
2
+
3
+ module PlcUtil
4
+ module Awl
5
+ class SymlistFile
6
+ def initialize(filename)
7
+ @symlist = {}
8
+ raise 'Specified symlist file not found' unless File.exists? filename
9
+ table = DBF::Table.new filename
10
+ table.each do |rec|
11
+ next unless rec
12
+ @symlist[rec.attributes['_skz']] = rec.attributes['_opiec']
13
+ end
14
+ end
15
+
16
+ def [](tag)
17
+ lookup tag
18
+ end
19
+
20
+ def lookup(tag)
21
+ @symlist[tag]
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+
28
+
@@ -0,0 +1,110 @@
1
+ require 'treetop'
2
+
3
+ module PlcUtil
4
+ module Awl
5
+ module AwlGrammar
6
+ module StructHelpers
7
+ def struct_visit_helper(struct_node)
8
+ struct_node.decl.elements.map do |el|
9
+ { :id => el.identifier.text_value }.tap do |result|
10
+ el.d.visit result
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ module TopLevelNode
17
+ def visit
18
+ {
19
+ :dbs => [],
20
+ :udts => [],
21
+ :obs => []
22
+ }.tap do |result|
23
+ elements.each {|e| e.visit result }
24
+ end
25
+ end
26
+ end
27
+
28
+ module DbNode
29
+ include StructHelpers
30
+ def visit(root)
31
+ root[:dbs] << {}.tap do |db|
32
+ name.hash_entry db
33
+ db[:entries] = struct_visit_helper root_struct_decl.struct_data_type
34
+ end
35
+ end
36
+ end
37
+
38
+ module UdtNode
39
+ include StructHelpers
40
+ def visit(root)
41
+ root[:udts] << {}.tap do |udt|
42
+ name.hash_entry udt
43
+ udt[:entries] = struct_visit_helper root_struct_decl.struct_data_type
44
+ end
45
+ end
46
+ end
47
+
48
+ module ObNode
49
+ def visit(root)
50
+ root[:obs] << {}.tap do |ob|
51
+ ob[:title] = title.optional_title.title_quoted.v.text_value
52
+ name.hash_entry ob
53
+ end
54
+ end
55
+ end
56
+
57
+
58
+ module ArrayDeclarationNode
59
+ def visit(declaration)
60
+ declaration[:array] = (ar_begin.text_value.to_i)..(ar_end.text_value.to_i)
61
+ declaration[:comment] = array_comment.line_comment.comment_body.text_value unless array_comment.terminal?
62
+ non_array_data_type.visit declaration
63
+ end
64
+ end
65
+
66
+ module NonArrayDeclarationNode
67
+ def visit(declaration)
68
+ declaration[:comment] = comment.comment_body.text_value unless comment.terminal?
69
+ non_array_data_type.visit declaration
70
+ end
71
+ end
72
+
73
+ module BasicDataTypeNode
74
+ def visit(declaration)
75
+ declaration[:data_type] = text_value.downcase.to_sym
76
+ end
77
+ end
78
+
79
+ module StructDataTypeNode
80
+ include StructHelpers
81
+ def visit(declaration)
82
+ declaration[:data_type] = {}.tap do |struct|
83
+ struct[:entries] = struct_visit_helper self
84
+ end
85
+ end
86
+ end
87
+
88
+ module UdtDataTypeNode
89
+ def visit(declaration)
90
+ declaration[:data_type] = v.text_value
91
+ end
92
+ end
93
+
94
+
95
+
96
+
97
+ module QuotedNameNode
98
+ def hash_entry(h)
99
+ h[:name] = v.text_value
100
+ end
101
+ end
102
+
103
+ module DbNameNode
104
+ def hash_entry(h)
105
+ h[:db_number] = number.to_s.to_i
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'clamp'
4
+ require 'plcutil/siemens/awl/awl_file'
5
+ require 'plcutil/siemens/awl/symlist_file'
6
+
7
+ module PlcUtil
8
+ # Command line tool to read and output an awl file
9
+ class AwlPrettyPrintRunner < Clamp::Command
10
+ def execute
11
+ opt = {
12
+ :symlist => symlist,
13
+ :blocks => Hash[block_name.split(/[,=]/)]
14
+ }
15
+
16
+ file_list.each do |filename|
17
+ process_awl_file Awl::AwlFile.new(filename, opt)
18
+ end
19
+ end
20
+
21
+ def fix_name(name)
22
+ if no_block?
23
+ name.sub /^[^\.]*\./, ''
24
+ else
25
+ name
26
+ end
27
+ end
28
+
29
+ def process_awl_file(awl)
30
+ awl.each_exploded do |addr, name, comment, type_name|
31
+ puts '%-11s %-40s%-10s%s' % [
32
+ addr.to_s,
33
+ fix_name(name),
34
+ type_name.to_s.upcase,
35
+ comment
36
+ ]
37
+ end
38
+ end
39
+
40
+ option %w(--no-block -n), :flag, "don't use datablock as part of the tagname"
41
+ option %w(--symlist -s), 'FILE', 'specify SYMLIST.DBF frfom Step 7 project'
42
+ option %w(--block-name -b), 'NAME=ADDR,NAME=ADDR', 'specify DB adress directly instead of using SYMLIST.DBF', :default => '' do |s|
43
+ raise 'Invalid format of block names, example use: A=DB20,B=DB21' unless s.match(/(\w+=\w+,)*(\w+=\w+)/)
44
+ end
45
+
46
+ parameter "FILE ...", 'awl files to read (exported inside Step 7)'
47
+ end
48
+ end
File without changes
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/ruby
2
2
 
3
3
  require 'optparse'
4
- require 'tallakt-plcutil/siemens/awlfile'
5
- require 'tallakt-plcutil/siemens/sdffile'
6
- require 'tallakt-plcutil/wonderware/intouchfile'
4
+ require 'plcutil/siemens/awl/awl_file'
5
+ require 'plcutil/siemens/sdffile'
6
+ require 'plcutil/wonderware/intouchfile'
7
7
 
8
8
  module PlcUtil
9
9
  # Command line tool to read and output an awl file
@@ -1,4 +1,4 @@
1
1
  module PlcUtil
2
- VERSION = '0.2.3'
2
+ VERSION = '0.2.2'
3
3
  end
4
4
 
@@ -1,6 +1,6 @@
1
1
  require 'rubygems'
2
2
  require 'optparse'
3
- require 'tallakt-plcutil/wonderware/intouchfile'
3
+ require 'plcutil/wonderware/intouchfile'
4
4
 
5
5
  module PlcUtil
6
6
  class IntouchPrettyPrintRunner
@@ -3,6 +3,6 @@ $:.unshift(File.dirname(__FILE__)) unless
3
3
  $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
4
4
 
5
5
  module PlcUtil
6
- VERSION = '0.2.2'
6
+ VERSION = '0.2.4'
7
7
  end
8
8
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tallakt-plcutil
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.2.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -13,37 +13,96 @@ date: 2010-10-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: dbf
16
- requirement: &270292080 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
- - - ! '>='
19
+ - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: 1.6.6
21
+ version: '1.6'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *270292080
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.6'
30
+ - !ruby/object:Gem::Dependency
31
+ name: polyglot
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '0.3'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '0.3'
46
+ - !ruby/object:Gem::Dependency
47
+ name: treetop
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '1.4'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.4'
62
+ - !ruby/object:Gem::Dependency
63
+ name: clamp
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '0.4'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: '0.4'
25
78
  description: Ruby library for using Siemens, Schneider and Intouch files
26
79
  email: tallak@tveide.net
27
80
  executables:
28
81
  - awlpp
29
82
  - intouchpp
30
83
  - pl7tointouch
31
- - s7tointouch
32
84
  extensions: []
33
85
  extra_rdoc_files:
34
86
  - LICENSE
35
87
  - README.rdoc
36
88
  files:
37
- - lib/tallakt-plcutil/schneider/pl7_to_intouch_runner.rb
38
- - lib/tallakt-plcutil/siemens/awlfile.rb
39
- - lib/tallakt-plcutil/siemens/awl_pretty_print_runner.rb
40
- - lib/tallakt-plcutil/siemens/sdffile.rb
41
- - lib/tallakt-plcutil/siemens/step7_to_intouch_runner.rb
42
- - lib/tallakt-plcutil/version.rb
43
- - lib/tallakt-plcutil/wonderware/intouchfile.rb
44
- - lib/tallakt-plcutil/wonderware/intouch_pretty_print_runner.rb
45
- - lib/tallakt-plcutil/wonderware/standard_sections.yaml
46
- - lib/tallakt-plcutil.rb
89
+ - lib/plcutil/schneider/pl7_to_intouch_runner.rb
90
+ - lib/plcutil/siemens/awl/array_type.rb
91
+ - lib/plcutil/siemens/awl/awl.treetop
92
+ - lib/plcutil/siemens/awl/awl_file.rb
93
+ - lib/plcutil/siemens/awl/basic_type.rb
94
+ - lib/plcutil/siemens/awl/db_address.rb
95
+ - lib/plcutil/siemens/awl/struct_type.rb
96
+ - lib/plcutil/siemens/awl/symlist_file.rb
97
+ - lib/plcutil/siemens/awl/treetop_nodes.rb
98
+ - lib/plcutil/siemens/awl_pretty_print_runner.rb
99
+ - lib/plcutil/siemens/sdffile.rb
100
+ - lib/plcutil/siemens/step7_to_intouch_runner.rb
101
+ - lib/plcutil/version.rb
102
+ - lib/plcutil/wonderware/intouchfile.rb
103
+ - lib/plcutil/wonderware/intouch_pretty_print_runner.rb
104
+ - lib/plcutil/wonderware/standard_sections.yaml
105
+ - lib/plcutil.rb
47
106
  - bin/awlpp
48
107
  - bin/intouchpp
49
108
  - bin/pl7tointouch
@@ -54,7 +113,6 @@ files:
54
113
  - History.txt
55
114
  - LICENSE
56
115
  - README.rdoc
57
- - Rakefile
58
116
  homepage: http://github.com/tallakt/plcutil
59
117
  licenses: []
60
118
  post_install_message:
@@ -76,7 +134,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
76
134
  version: '0'
77
135
  requirements: []
78
136
  rubyforge_project:
79
- rubygems_version: 1.8.15
137
+ rubygems_version: 1.8.24
80
138
  signing_key:
81
139
  specification_version: 3
82
140
  summary: Ruby PLC file library
data/Rakefile DELETED
@@ -1,36 +0,0 @@
1
- require 'rubygems'
2
- require 'rake'
3
-
4
- require 'rake/testtask'
5
- Rake::TestTask.new(:test) do |test|
6
- test.libs << 'lib' << 'test'
7
- test.pattern = 'test/**/test_*.rb'
8
- test.verbose = true
9
- end
10
-
11
- begin
12
- require 'rcov/rcovtask'
13
- Rcov::RcovTask.new do |test|
14
- test.libs << 'test'
15
- test.pattern = 'test/**/test_*.rb'
16
- test.verbose = true
17
- end
18
- rescue LoadError
19
- task :rcov do
20
- abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
21
- end
22
- end
23
-
24
- task :test => :check_dependencies
25
-
26
- task :default => :test
27
-
28
- require 'rake/rdoctask'
29
- Rake::RDocTask.new do |rdoc|
30
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
31
-
32
- rdoc.rdoc_dir = 'rdoc'
33
- rdoc.title = "tallakt-plcutil #{version}"
34
- rdoc.rdoc_files.include('README*')
35
- rdoc.rdoc_files.include('lib/**/*.rb')
36
- end
@@ -1,87 +0,0 @@
1
- #!/usr/bin/ruby
2
-
3
- require 'optparse'
4
- require 'tallakt-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
- @no_block = false
16
-
17
- option_parser.parse! args
18
- if args.size != 1
19
- show_help
20
- exit
21
- end
22
- filename, = args
23
- @awl = AwlFile.new filename, @awloptions
24
- if @output
25
- File.open @output, 'w' do |f|
26
- print_to_file f
27
- end
28
- else
29
- print_to_file $stdout
30
- end
31
- end
32
-
33
- def fix_name(name)
34
- if @no_block
35
- name.sub /^[^\.]*\./, ''
36
- else
37
- name
38
- end
39
- end
40
-
41
- def print_to_file(f)
42
- @awl.each_tag do |tag| #|name, data_block_name, addr, comment, struct_comment, type|
43
- require 'rubygems'
44
- require 'pp'
45
- f.puts @format % [
46
- tag[:addr],
47
- fix_name(tag[:name]),
48
- tag[:type].to_s,
49
- [tag[:comment], tag[:struct_comment]].compact! ? '' : (@commentformat % [tag[:comment], tag[:struct_comment]])
50
- ]
51
- end
52
- end
53
-
54
- def option_parser
55
- OptionParser.new do |opts|
56
- opts.banner = "Usage: awlpp [options] AWLFILE"
57
- opts.on("-c", "--csv", String, "Output as CSV file") do
58
- format = '%s;%s;%s;%s'
59
- @commentformat = '%s'
60
- end
61
- opts.on("-n", "--no-block", String, "Dont use the datablock as part of the tag", "name") do
62
- @no_block = true
63
- end
64
- opts.on("-s", "--symlist FILE", String, "Specify SYMLIST.DBF file from S7 project ") do |symlistfile|
65
- @awloptions[:symlist] = symlistfile
66
- end
67
- opts.on("-b", "--block NAME=ADDR", String, "Define address of datablock without", "reading symlist") do |blockdef|
68
- name, addr = blockdef.split(/=/)
69
- @awloptions[:blocks] ||= {}
70
- @awloptions[:blocks][name] = addr
71
- end
72
- opts.on("-o", "--output FILE", String, "Output to specified file instead of", "standard output") do |output|
73
- @output = output
74
- end
75
- opts.on_tail("-h", "--help", "Show this message") do
76
- puts opts
77
- exit
78
- end
79
- end
80
- end
81
-
82
- def show_help
83
- puts option_parser
84
- end
85
-
86
- end
87
- end
@@ -1,295 +0,0 @@
1
- #!/usr/bin/ruby
2
-
3
-
4
- module PlcUtil
5
- # Reads a Siemens Step 7 AWL file and creates single tags with addresses and comment
6
- class AwlFile
7
- attr_reader :symlist
8
-
9
- def initialize(filename, options = {})
10
- @types = {}
11
- init_basic_types
12
- @datablocks = []
13
- @symlist = {}
14
-
15
- if options[:symlist]
16
- require 'rubygems'
17
- require 'dbf' or raise 'Please install gem dbf to read symlist file'
18
-
19
- raise 'Specified symlist file not found' unless File.exists? options[:symlist]
20
- table = DBF::Table.new(options[:symlist])
21
- table.each do |rec|
22
- next unless rec
23
- @symlist[rec.attributes['_skz']] = rec.attributes['_opiec'] # or _ophist or _datatyp
24
- end
25
- end
26
-
27
- @symlist.merge!(options[:blocks] || {}) # User may override blocks
28
-
29
- File.open filename do |f|
30
- parse f
31
- end
32
- end
33
-
34
- def each_tag(options = {})
35
- @datablocks.each do |var|
36
- name = var.name
37
- if options[:no_block]
38
- name = nil
39
- end
40
- var.type.explode(Address.new(0, data_block_address(var.name)), name, options).each do |item|
41
- ii = item.clone
42
- ii[:type] = item[:type].downcase.to_sym
43
- yield ii
44
- end
45
- end
46
- end
47
-
48
- private
49
-
50
- def data_block_address(name)
51
- if @symlist.key? name
52
- @symlist[name].gsub /\s+/, ''
53
- else
54
- nil
55
- end
56
- end
57
-
58
- def init_basic_types
59
- types = {
60
- 'BOOL' => 1,
61
- 'BYTE' => 8,
62
- 'CHAR' => 8,
63
- 'DATE' => 2 * 8,
64
- 'DINT' => 4 * 8,
65
- 'DWORD' => 4 * 8,
66
- 'INT' => 2 * 8,
67
- 'REAL' => 4 * 8,
68
- 'S5TIME' => 2 * 8,
69
- 'TIME' => 4 * 8,
70
- 'TIME_OF_DAY' => 4 * 8,
71
- 'WORD' => 2 * 8,
72
- 'DATE_AND_TIME' => 4 * 8, # ??? only used in VAR_TEMP
73
- 'TIMER' => 1, # Only in FC calls
74
- 'CONT_C' => 125 * 8,
75
- }
76
- types.each {|name, size| add_type BasicType.new(name, size) }
77
- end
78
-
79
- def add_type(type)
80
- @types[type.name] = type
81
- end
82
-
83
- def lookup_type(type)
84
- raise "Could not find type '#{type}'" unless @types.key? type
85
- @types[type]
86
- end
87
-
88
- def parse(file)
89
- stack = []
90
- in_array_decl = false # array decalrations are sometimed split on two separate lines
91
- in_datablock_decl = false
92
- tagname = start = stop = type = comment = nil
93
- db_to_fb = {} # a list connecting a DB to the FB where it was used
94
- file.each_line do |l|
95
- l.chomp!
96
- if in_array_decl
97
- if l.match '^\s+\"?([A-Za-z0-9_]+)"?\s?;'
98
- type = $1
99
- stack.last.add Variable.new(tagname, ArrayType.new(@types[type], start..stop), comment)
100
- end
101
- in_array_decl = false
102
- else
103
- case l
104
- # TODO should also cater for 'DB 90' type addresses
105
- when /^TYPE "(.+?)"/
106
- stack = [StructType.new($1, :datatype)]
107
- add_type stack.first
108
- when /^FUNCTION_BLOCK "(.+?)"/
109
- stack = [StructType.new($1, :functionblock)]
110
- add_type stack.first
111
- when /^DATA_BLOCK ("(.+?)"|DB\s+(\d+))/
112
- in_datablock_decl = true
113
- name = $2 || ('DB' + $3)
114
- stack = [StructType.new(name, :datablock)]
115
- @datablocks << Variable.new(name, stack.last)
116
- when /^VAR_TEMP/
117
- s = StructType.new('VAR_TEMP', :anonymous)
118
- stack = [s]
119
- when /^\s*(\S+) : STRUCT /
120
- in_datablock_decl = false
121
- s = StructType.new('STRUCT', :anonymous)
122
- stack.last.add Variable.new($1, s)
123
- stack << stack.last.children.last.type
124
- when /^BEGIN$/
125
- in_datablock_decl = false
126
- when /^\s+BEGIN/
127
- stack.pop
128
- # New variable in struct or data block
129
- when /^\s+([A-Za-z0-9_ ]+) : "?([A-Za-z0-9_ ]+?)"?\s*(:=\s*[^;]+)?;(\s*\/\/(.*))?/
130
- if stack.any?
131
- tagname, type_name, comment = $1, $2, ($5 || '')
132
- stack.last.add Variable.new(tagname, lookup_type(type_name), comment)
133
- end
134
- when /^\s+([A-Za-z0-9_]+) : ARRAY\s*\[(\d+)\D+(\d+) \] OF "?([A-Za-z0-9_]+)"?\s?;(\s*\/\/(.*))?/
135
- tagname, start, stop, type, comment = $1, $2, $3, $4, $6
136
- stack.last.add Variable.new(tagname, ArrayType.new(lookup_type(type), start..stop), comment)
137
- when /^\s+([A-Za-z0-9_]+) : ARRAY\s*\[(\d+)\D+(\d+) \] OF(\s?\/\/(.*))?$/
138
- tagname, start, stop, comment = $1, $2, $3, $4
139
- in_array_decl = true
140
- when /^"(.*?)"$/
141
- # datablock definition is stored in FB definition
142
- # remember which FB to use but assign the FB
143
- # only after all blocks have been loaded
144
- db_to_fb[stack.first.name] = $1 if in_datablock_decl
145
- end
146
- end
147
- end
148
-
149
- # Update FB to DB use
150
- @datablocks.each do |db|
151
- if db_to_fb.key? db.name
152
- fb = lookup_type db_to_fb[db.name]
153
- fb.children.each {|child| db.type.add child } if fb.respond_to? :children
154
- end
155
- end
156
- end
157
-
158
- def defined_type(type)
159
- @types.key?(type.name)
160
- end
161
-
162
-
163
- class BasicType
164
- attr_accessor :bit_size, :name
165
-
166
- def initialize(name, bit_size)
167
- @bit_size, @name = bit_size, name
168
- end
169
-
170
- def explode(start_addr, name, comment, struct_comment, options = {})
171
- actual_start = start_addr.next_start bit_size
172
- [{:addr => actual_start, :name => name,
173
- :struct_comment => struct_comment, :comment => comment, :type => @name}]
174
- end
175
-
176
- def end_address(start_address)
177
- start_address.next_start(bit_size).skip! bit_size
178
- end
179
- end
180
-
181
-
182
-
183
- class StructType
184
- attr_accessor :name, :children, :type
185
-
186
- def initialize(name = 'STRUCT', type = :anonymous)
187
- @name = name
188
- @children = []
189
- @type = type
190
- end
191
-
192
- def add(child)
193
- raise 'Added nil child' unless child
194
- raise 'Added nil child type' unless child.type
195
- @children << child
196
- end
197
-
198
- def end_address(start_address)
199
- addr = start_address.next_start
200
- @children.each do |child|
201
- addr = child.type.end_address addr
202
- end
203
- addr.next_start!
204
- end
205
-
206
- def explode(start_addr, name, comment = nil, struct_comment = nil, options = {})
207
- addr = start_addr.next_start
208
- exploded = []
209
- @children.each do |child|
210
-
211
- exploded += child.type.explode(addr, [name, child.name].compact.join('.'), child.comment,
212
- type == :datablock ? child.comment : struct_comment, options)
213
- addr = child.type.end_address addr
214
- end
215
- exploded
216
- end
217
- end
218
-
219
- class ArrayType
220
- attr_accessor :range, :type
221
-
222
- def initialize(type, range)
223
- raise 'Added nil array type' unless type
224
- raise 'Added nil array range' unless range
225
- @range, @type = range, type
226
- end
227
-
228
- def name
229
- 'ARRAY'
230
- end
231
-
232
- def end_address(start_address)
233
- addr = start_address.next_start
234
- range.each do
235
- addr = type.end_address addr
236
- end
237
- addr
238
- end
239
-
240
- def explode(start_addr, name, comment, struct_comment, options = {})
241
- exploded = []
242
- addr = start_addr.next_start
243
- range.to_a.each_with_index do |v, i|
244
- exploded += type.explode(addr, name + '[' + v.to_s + ']', comment, struct_comment, options = {})
245
- addr = type.end_address(addr)
246
- end
247
- exploded
248
- end
249
- end
250
-
251
- class Variable
252
- attr_accessor :name, :type, :comment
253
-
254
- def initialize(name, type, comment = nil)
255
- @name, @type, @comment= name, type, comment
256
- end
257
- end
258
-
259
- class Address
260
- attr_accessor :bit_addr, :data_block_addr
261
-
262
- def initialize(bit_addr, data_block_addr = nil)
263
- @bit_addr, @data_block_addr = bit_addr, data_block_addr
264
- end
265
-
266
- def to_s
267
- (data_block_addr || 'DB???') + ',' + byte.to_s + '.' + bit.to_s
268
- end
269
-
270
- def bit
271
- @bit_addr % 8
272
- end
273
-
274
- def byte
275
- @bit_addr / 8
276
- end
277
-
278
- def next_start!(bit_size = 2 * 8)
279
- bs = [bit_size, 2 * 8].min # bools use bits, chars/bytes one byte others two byte rounding of start address
280
- rest = @bit_addr % bs
281
- @bit_addr += (bs - rest) if rest > 0
282
- self
283
- end
284
-
285
- def next_start(bit_size = 2 * 8)
286
- self.clone.next_start! bit_size
287
- end
288
-
289
- def skip!(bit_count)
290
- @bit_addr += bit_count
291
- self
292
- end
293
- end
294
- end
295
- end