tallakt-plcutil 0.2.3 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
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