ladder_drive 0.6.3 → 0.6.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ebbe6a801c62fb7bf390b50e904186de6c8a54d4
4
- data.tar.gz: 65bc6883680145868f3bec1b333d76760fee2eb0
3
+ metadata.gz: 78f994329616c81aa08af6f821851a9cfa302382
4
+ data.tar.gz: cdd5b03d754155febbc0e0b77f4e23a09910a447
5
5
  SHA512:
6
- metadata.gz: 2a78ed56aecb16bb8a4bdf5275acdf6b5f551cc62bce2efd6512160e79aa5c0d9757eb9eed6419f419bb5eba4f13fcc44e6ead4d4b9cc6cdb4cb4277b8ad150b
7
- data.tar.gz: 885041e935159cc775be45f6f8d0d46a148f74739c7b24fea1ccf8c4f4e5627f3be0b0b1cbbdd2fb92c6176defefadb949e55cf8e25848a76d10dde54c577f49
6
+ metadata.gz: c92d94494040f7d972110d07d116df9fdea309c979fde59a58da644e4f9d41a775ec680c16a8e82be9267705f6dae9f6ca7967b4c595c03dcbf1cbd41158081f
7
+ data.tar.gz: d5343de39d9760831bda75adb6dfc7e6f30cb485f7c6f137a67dcd651583ecc517c415de040963cbe6cab29f5a968d1fdd7a9837fc9ebdc69ace1dfedbc8b07d
data/Gemfile CHANGED
@@ -3,9 +3,13 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in ladder_drive.gemspec
4
4
  gemspec
5
5
 
6
- gem "test-unit"
6
+ group :test do
7
+ gem "test-unit"
8
+ gem "test-unit-rr"
9
+ #gem "test-unit-notify"
10
+ end
7
11
 
8
- gem "activesupport", '~> 5.0'
12
+ gem "activesupport", '>= 4.0'
9
13
 
10
14
  gem 'pi_piper', ">= 2.0.0"
11
15
  gem "ffi", "~> 1.9.24"
@@ -13,3 +17,4 @@ gem "ffi", "~> 1.9.24"
13
17
  gem 'serialport'
14
18
  gem 'google_drive'
15
19
  gem "ruby-trello"
20
+ gem 'ambient_iot', ">= 0.1.1"
@@ -22,6 +22,7 @@ Gem::Specification.new do |spec|
22
22
  spec.add_runtime_dependency 'google_drive', '~> 3.0'
23
23
  spec.add_runtime_dependency 'ruby-trello', '~>2.1'
24
24
 
25
+ spec.required_ruby_version = '>= 2.3.3'
25
26
 
26
27
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
27
28
  spec.bindir = "exe"
@@ -28,6 +28,7 @@ require 'protocol/protocol'
28
28
 
29
29
  include LadderDrive::Protocol::Mitsubishi
30
30
  include LadderDrive::Protocol::Keyence
31
+ include LadderDrive::Protocol::Omron
31
32
  include LadderDrive::Protocol::Emulator
32
33
 
33
34
  module LadderDrive
@@ -27,6 +27,7 @@ require 'protocol/protocol'
27
27
 
28
28
  include LadderDrive::Protocol::Mitsubishi
29
29
  include LadderDrive::Protocol::Keyence
30
+ include LadderDrive::Protocol::Omron
30
31
  include LadderDrive::Protocol::Emulator
31
32
 
32
33
  module LadderDrive
@@ -125,6 +125,17 @@ module LadderDrive
125
125
  suffixes_for_input.include? @suffix
126
126
  end
127
127
 
128
+ def value
129
+ case @value
130
+ when true
131
+ 1
132
+ when false, nil
133
+ 0
134
+ else
135
+ @value
136
+ end
137
+ end
138
+
128
139
  def bool
129
140
  case @value
130
141
  when Integer
@@ -50,11 +50,6 @@ module Keyence
50
50
  @socket = nil
51
51
  end
52
52
 
53
- def get_bit_from_device device
54
- device = device_by_name device
55
- get_bits_from_device(1, device).first
56
- end
57
-
58
53
  def get_bits_from_device count, device
59
54
  c = (count + 15) / 16
60
55
  words = get_words_from_device c, device
@@ -89,11 +84,6 @@ module Keyence
89
84
  alias :set_bit_to_device :set_bits_to_device
90
85
 
91
86
 
92
- def get_word_from_device device
93
- device = device_by_name device
94
- get_words_from_device(1, device).first
95
- end
96
-
97
87
  def get_words_from_device(count, device)
98
88
  device = local_device device
99
89
  packet = "RDS #{device.name}.H #{count}\r\n"
@@ -79,11 +79,6 @@ module Mitsubishi
79
79
  @comm = nil
80
80
  end
81
81
 
82
- def get_bit_from_device device
83
- device = device_by_name device
84
- get_bits_from_device(1, device).first
85
- end
86
-
87
82
  def get_bits_from_device count, device
88
83
  raise ArgumentError.new("A count #{count} must be between #{available_bits_range.first} and #{available_bits_range.last} for #{__method__}") unless available_bits_range.include? count
89
84
 
@@ -130,11 +125,6 @@ module Mitsubishi
130
125
  end
131
126
  end
132
127
 
133
- def get_word_from_device device
134
- device = device_by_name device
135
- get_words_from_device(1, device).first
136
- end
137
-
138
128
  def get_words_from_device(count, device)
139
129
  raise ArgumentError.new("A count #{count} must be between #{available_words_range.first} and #{available_words_range.last} for #{__method__}") unless available_words_range.include? count
140
130
 
@@ -50,11 +50,6 @@ module Mitsubishi
50
50
  @socket = nil
51
51
  end
52
52
 
53
- def get_bit_from_device device
54
- device = device_by_name device
55
- get_bits_from_device(1, device).first
56
- end
57
-
58
53
  def get_bits_from_device count, device
59
54
  raise ArgumentError.new("A count #{count} must be between #{available_bits_range.first} and #{available_bits_range.last} for #{__method__}") unless available_bits_range.include? count
60
55
 
@@ -108,11 +103,6 @@ module Mitsubishi
108
103
  end
109
104
 
110
105
 
111
- def get_word_from_device device
112
- device = device_by_name device
113
- get_words_from_device(1, device).first
114
- end
115
-
116
106
  def get_words_from_device(count, device)
117
107
  raise ArgumentError.new("A count #{count} must be between #{available_words_range.first} and #{available_words_range.last} for #{__method__}") unless available_bits_range.include? count
118
108
 
@@ -0,0 +1,224 @@
1
+ # The MIT License (MIT)
2
+ #
3
+ # Copyright (c) 2019 ITO SOFT DESIGN Inc.
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining
6
+ # a copy of this software and associated documentation files (the
7
+ # "Software"), to deal in the Software without restriction, including
8
+ # without limitation the rights to use, copy, modify, merge, publish,
9
+ # distribute, sublicense, and/or sell copies of the Software, and to
10
+ # permit persons to whom the Software is furnished to do so, subject to
11
+ # the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ module LadderDrive
25
+ module Protocol
26
+ module Omron
27
+
28
+ class CModeProtocol < Protocol
29
+
30
+ attr_accessor :baudrate
31
+ attr_accessor :unit_no
32
+
33
+ DELIMITER = "\r"
34
+ TERMINATOR = "*\r"
35
+ TIMEOUT = 1.0
36
+
37
+ def initialize options={}
38
+ super
39
+ @port = options[:port] || `ls /dev/tty.usb*`.split("\n").map{|l| l.chomp}.first
40
+ @baudrate = 38400
41
+ @unit_no = 0
42
+ @comm = nil
43
+ #prepare_device_map
44
+ end
45
+
46
+ def open
47
+ open!
48
+ rescue
49
+ nil
50
+ end
51
+
52
+ def open!
53
+ return false unless @port
54
+ begin
55
+ # port, baudrate, bits, stop bits, parity(0:none, 1:even, 2:odd)
56
+ @comm ||= SerialPort.new(@port, @baudrate, 7, 2, 1).tap do |s|
57
+ s.read_timeout = (TIMEOUT * 1000.0).to_i
58
+ end
59
+ rescue => e
60
+ p e
61
+ nil
62
+ end
63
+ end
64
+
65
+ def close
66
+ @comm.close if @comm
67
+ @comm = nil
68
+ end
69
+
70
+ def unit_no= no
71
+ @unit_no = [[no, 0].max, 31].min
72
+ end
73
+
74
+ def get_bits_from_device count, device
75
+ device = device_by_name device
76
+
77
+ # convert to the channel device
78
+ from = device.channel_device
79
+ to = (device + count).channel_device
80
+ c = [to - from, 1].max
81
+
82
+ # get value as words
83
+ words = get_words_from_device(c, device)
84
+
85
+ # convert to bit devices
86
+ index = device.bit
87
+ bits = []
88
+ count.times do
89
+ i = index / 16
90
+ b = index % 16
91
+ f = 1 << b
92
+ bits << ((words[i] & f) == f)
93
+ index += 1
94
+ end
95
+ bits
96
+ end
97
+
98
+ def get_words_from_device(count, device)
99
+ device = device_by_name(device).channel_device
100
+
101
+ # make read packet
102
+ packet = read_packet_with device
103
+ packet << "#{device.channel.to_s.rjust(4, '0')}#{count.to_s.rjust(4, '0')}"
104
+ packet << fcs_for(packet).to_s(16).upcase.rjust(2, "0")
105
+ packet << TERMINATOR
106
+ @logger.debug("> #{dump_packet packet}")
107
+
108
+ # send command
109
+ open
110
+ send packet
111
+
112
+ # receive response
113
+ words = []
114
+ terminated = false
115
+ loop do
116
+ res = receive
117
+ data = ""
118
+ if res
119
+ ec = error_code(res)
120
+ raise "Error response: #{ec.to_i(16).rjust(2, '0')}" unless ec == 0
121
+ if res[-2,2] == TERMINATOR
122
+ fcs = fcs_for(res[0..-5])
123
+ raise "Not matched FCS expected #{fcs.to_s(16).rjust(2,'0')}" unless fcs == res[-4,2].to_i(16)
124
+ data = res[7..-5]
125
+ terminated = true
126
+ else res[-1,1] == DELIMITER
127
+ fcs = fcs_for(res[0..-4])
128
+ raise "Not matched FCS expected #{fcs.to_s(16).rjust(2,'0')}" unless fcs == res[-3,2].to_i(16)
129
+ data = res[7..-4]
130
+ end
131
+ len = data.length
132
+ index = 0
133
+ while index < len
134
+ words << data[index,4].to_i(16)
135
+ index += 4
136
+ end
137
+ return words if terminated
138
+ else
139
+ break
140
+ end
141
+ end
142
+ []
143
+ end
144
+
145
+ private
146
+
147
+ def device_by_name name
148
+ case name
149
+ when String
150
+ d = OmronDevice.new name
151
+ d.valid? ? d : nil
152
+ when EscDevice
153
+ local_device_of name
154
+ else
155
+ # it may be already OmronDevice
156
+ name
157
+ end
158
+ end
159
+
160
+ def send packet
161
+ @comm.write(packet)
162
+ @comm.flush
163
+ end
164
+
165
+ def receive
166
+ res = ""
167
+ begin
168
+ Timeout.timeout(TIMEOUT) do
169
+ res = @comm.gets DELIMITER
170
+ =begin
171
+ loop do
172
+ res << @comm.getc# '\r' #gets
173
+ break if res[-1] == '\r'
174
+ end
175
+ =end
176
+ end
177
+ # res
178
+ rescue Timeout::Error
179
+ puts "*** ERROR: TIME OUT : #{res} ***"
180
+ end
181
+ @logger.debug("< #{dump_packet res}")
182
+ res
183
+ end
184
+
185
+
186
+ def read_packet_with device
187
+ packet = "@#{unit_no.to_s.rjust(2, '0')}R"
188
+ case device.suffix
189
+ when "HR"
190
+ packet << "H"
191
+ when "AL"
192
+ packet << "L"
193
+ when "DM", "D"
194
+ packet << "D"
195
+ when "AR"
196
+ packet << "J"
197
+ when "EM", "E"
198
+ packet << "E"
199
+ else
200
+ packet << "R"
201
+ end
202
+ end
203
+
204
+ def fcs_for packet
205
+ fcs = packet.bytes.inject(0) do |a, b|
206
+ a = a ^ b
207
+ end
208
+ fcs = fcs & 0xff
209
+ fcs
210
+ end
211
+
212
+ def error_code packet
213
+ packet[1 + 2 + 2, 2].to_i(16)
214
+ end
215
+
216
+ def dump_packet packet
217
+ packet.inspect
218
+ end
219
+
220
+ end
221
+
222
+ end
223
+ end
224
+ end
@@ -0,0 +1,378 @@
1
+ # The MIT License (MIT)
2
+ #
3
+ # Copyright (c) 2019 ITO SOFT DESIGN Inc.
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining
6
+ # a copy of this software and associated documentation files (the
7
+ # "Software"), to deal in the Software without restriction, including
8
+ # without limitation the rights to use, copy, modify, merge, publish,
9
+ # distribute, sublicense, and/or sell copies of the Software, and to
10
+ # permit persons to whom the Software is furnished to do so, subject to
11
+ # the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be
14
+ # included in all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ module LadderDrive
25
+ module Protocol
26
+ module Omron
27
+
28
+ class FinsTcpProtocol < Protocol
29
+
30
+ attr_accessor :gateway_count
31
+ attr_accessor :destination_network
32
+ attr_accessor :destination_node
33
+ attr_accessor :destination_unit
34
+ attr_accessor :source_network
35
+ attr_accessor :source_node
36
+ attr_accessor :source_unit
37
+
38
+ attr_accessor :ethernet_module
39
+
40
+ attr_accessor :tcp_error_code
41
+
42
+ IOFINS_DESTINATION_NODE_FROM_IP = 0
43
+ IOFINS_SOURCE_AUTO_NODE = 0
44
+
45
+ # Available ethernet module.
46
+ ETHERNET_ETN21 = 0
47
+ ETHERNET_CP1E = 1
48
+ ETHERNET_CP1L = 2
49
+ ETHERNET_CP1H = 3
50
+
51
+ def initialize options={}
52
+ super
53
+ @socket = nil
54
+ @host = options[:host] || "192.168.250.1"
55
+ @port = options[:port] || 9600
56
+ @gateway_count = 3
57
+ @destination_network = 0
58
+ @destination_node = 0
59
+ @destination_unit = 0
60
+ @source_network = 0
61
+ @source_node = IOFINS_SOURCE_AUTO_NODE
62
+ @source_unit = 0
63
+ @ethernet_module = ETHERNET_ETN21
64
+
65
+ @tcp_error_code = 0
66
+
67
+ prepare_device_map
68
+ end
69
+
70
+ def open
71
+ open!
72
+ rescue =>e
73
+ p e
74
+ nil
75
+ end
76
+
77
+ def open!
78
+ if @socket.nil?
79
+ @socket = TCPSocket.open(@host, @port)
80
+ if @socket
81
+ source_node = IOFINS_SOURCE_AUTO_NODE
82
+ query_node
83
+ end
84
+ end
85
+ @socket
86
+ end
87
+
88
+ def close
89
+ @socket.close if @socket
90
+ @socket = nil
91
+ end
92
+
93
+ def tcp_error?
94
+ tcp_error_code != 0
95
+ end
96
+
97
+ def create_query_node
98
+ header = [ "FINS".bytes.to_a, 0, 0, 0, 0xc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0].flatten
99
+ header[19] = source_node == IOFINS_SOURCE_AUTO_NODE ? 0 : source_node
100
+ header
101
+ end
102
+
103
+ def create_fins_frame packet
104
+ packet = packet.flatten
105
+ header = [ "FINS".bytes.to_a, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0].flatten
106
+ header[4, 4] = int_to_a(packet.length + 8, 4)
107
+ header + packet
108
+ end
109
+
110
+ def get_bits_from_device(count, device)
111
+ open
112
+ raise ArgumentError.new("A count #{count} must be between #{available_bits_range.first} and #{available_bits_range.last} for #{__method__}") unless available_bits_range.include? count
113
+
114
+ device = device_by_name device
115
+ raise ArgumentError.new("#{device.name} is not bit device!") unless device.bit_device?
116
+
117
+ command = [1, 1]
118
+ command << device_to_a(device)
119
+ command << int_to_a(count, 2)
120
+
121
+ send_packet create_fins_frame(fins_header + command)
122
+ res = receive
123
+
124
+ count.times.inject([]) do |a, i|
125
+ a << (res[16 + 10 + 4 + i] == 0 ? false : true)
126
+ a
127
+ end
128
+ end
129
+
130
+ def get_words_from_device(count, device)
131
+ open
132
+ raise ArgumentError.new("A count #{count} must be between #{available_words_range.first} and #{available_words_range.last} for #{__method__}") unless available_words_range.include? count
133
+
134
+ device = device_by_name device
135
+ device = device.channel_device
136
+
137
+ command = [1, 1]
138
+ command << device_to_a(device)
139
+ command << int_to_a(count, 2)
140
+
141
+ send_packet create_fins_frame(fins_header + command)
142
+ res = receive
143
+ count.times.inject([]) do |a, i|
144
+ a << to_int(res[16 + 10 + 4 + i * 2, 2])
145
+ a
146
+ end
147
+ end
148
+
149
+ def set_bits_to_device(bits, device)
150
+ open
151
+ count = bits.size
152
+ raise ArgumentError.new("A count #{count} must be between #{available_bits_range.first} and #{available_bits_range.last} for #{__method__}") unless available_bits_range.include? count
153
+
154
+ device = device_by_name device
155
+ raise ArgumentError.new("#{device.name} is not bit device!") unless device.bit_device?
156
+
157
+ command = [1, 2]
158
+ command << device_to_a(device)
159
+ command << int_to_a(count, 2)
160
+ bits.each do |b|
161
+ command << (b ? 1 : 0)
162
+ end
163
+
164
+ send_packet create_fins_frame(fins_header + command)
165
+ res = receive
166
+ end
167
+
168
+ def set_words_to_device(words, device)
169
+ open
170
+ count = words.size
171
+ raise ArgumentError.new("A count #{count} must be between #{available_words_range.first} and #{available_words_range.last} for #{__method__}") unless available_words_range.include? count
172
+
173
+ device = device_by_name device
174
+ device = device.channel_device
175
+
176
+ command = [1, 2]
177
+ command << device_to_a(device)
178
+ command << int_to_a(count, 2)
179
+ words.each do |w|
180
+ command << int_to_a(w, 2)
181
+ end
182
+
183
+ send_packet create_fins_frame(fins_header + command)
184
+ res = receive
185
+ end
186
+
187
+ def query_node
188
+ send_packet create_query_node
189
+ res = receive
190
+ self.source_node = res[19]
191
+ end
192
+
193
+
194
+ def send_packet packet
195
+ @socket.write(packet.flatten.pack("c*"))
196
+ @socket.flush
197
+ @logger.debug("> #{dump_packet packet}")
198
+ end
199
+
200
+ def receive
201
+ res = []
202
+ len = 0
203
+ begin
204
+ Timeout.timeout(5.0) do
205
+ loop do
206
+ c = @socket.getc
207
+ next if c.nil? || c == ""
208
+
209
+ res << c.bytes.first
210
+ next if res.length < 8
211
+
212
+ len = to_int(res[4, 4])
213
+ next if res.length < 8 + len
214
+
215
+ tcp_command = to_int(res[8, 4])
216
+ case tcp_command
217
+ when 3 # ERROR
218
+ raise "Invalidate tcp header: #{res}"
219
+ end
220
+ break
221
+ end
222
+ end
223
+ raise "Response error code: #{res[15]}" unless res[15] == 0
224
+ res
225
+ end
226
+ @logger.debug("< #{dump_packet res}")
227
+ res
228
+ end
229
+
230
+ # max length:
231
+ # CS1W-ETN21, CJ1W-ETN21 : 2012
232
+ # CP1W-CIF41 option board : 540 (1004 if cpu is CP1L/H)
233
+
234
+ def available_bits_range device=nil
235
+ case ethernet_module
236
+ when ETHERNET_ETN21
237
+ 1..(2012 - 8)
238
+ when ETHERNET_CP1E
239
+ 1..(540 - 8)
240
+ when ETHERNET_CP1L, ETHERNET_CP1H
241
+ 1..(1004 - 8)
242
+ else
243
+ 0..0
244
+ end
245
+ end
246
+
247
+ def available_words_range device=nil
248
+ case ethernet_module
249
+ when ETHERNET_ETN21
250
+ 1..((2012 - 8) / 2)
251
+ when ETHERNET_CP1E
252
+ 1..((540 - 8) / 2)
253
+ when ETHERNET_CP1L, ETHERNET_CP1H
254
+ 1..((1004 - 8) / 2)
255
+ else
256
+ 0..0
257
+ end
258
+ end
259
+
260
+ def device_by_name name
261
+ case name
262
+ when String
263
+ d = OmronDevice.new name
264
+ d.valid? ? d : nil
265
+ when EscDevice
266
+ local_device_of name
267
+ else
268
+ # it may be already OmronDevice
269
+ name
270
+ end
271
+ end
272
+
273
+ private
274
+
275
+ def fins_header
276
+ buf = [
277
+ 0x80, # ICF
278
+ 0x00, # RSV
279
+ 0x02, # GCT
280
+ 0x00, # DNA
281
+ 0x01, # DA1
282
+ 0x00, # DA2
283
+ 0x00, # SNA
284
+ 0x01, # SA1
285
+ 0x00, # SA2
286
+ 0x00, # SID
287
+ ]
288
+ buf[2] = gateway_count - 1
289
+ buf[3] = destination_network
290
+ if destination_node == IOFINS_DESTINATION_NODE_FROM_IP
291
+ buf[4] = destination_ipv4.split(".").last.to_i
292
+ else
293
+ buf[4] = destination_node
294
+ end
295
+ buf[7] = source_node
296
+ buf[8] = source_unit
297
+
298
+ buf
299
+ end
300
+
301
+ def fins_tcp_cmnd_header
302
+ header = [ "FINS".bytes.to_a, 0, 0, 0, 0xc, 0, 0, 0, 2, 0, 0, 0, 0].flatten
303
+ header[19] = source_node == IOFINS_SOURCE_AUTO_NODE ? 0 : source_node
304
+ header
305
+ end
306
+
307
+ def device_code_of device
308
+ @@bit_codes ||= { nil => 0x30, "" => 0x30, "W" => 0x31, "H" => 0x32, "A" => 0x33, "T" => 0x09, "C" => 0x09, "D" => 0x02, "E" => 0x0a, "TK" => 0x06 }
309
+ @@word_codes ||= { nil => 0xB0, "" => 0xB0, "W" => 0xB1, "H" => 0xB2, "A" => 0xB3, "TIM" => 0x89, "CNT" => 0x89, "D" => 0x82, "E" => 0x98, "DR" => 0xbc }
310
+ if device.bit_device?
311
+ @@bit_codes[device.suffix]
312
+ else
313
+ @@word_codes[device.suffix]
314
+ end
315
+ end
316
+
317
+ def device_to_a device
318
+ a = []
319
+ a << device_code_of(device)
320
+ a << int_to_a(device.channel, 2)
321
+ a << (device.bit_device? ? (device.bit || 0) : 0)
322
+ a.flatten
323
+ end
324
+
325
+
326
+ # FIXME: It's dummy currently.
327
+ def prepare_device_map
328
+ @conv_dev_dict ||= begin
329
+ h = {}
330
+ [
331
+ ["X", "0.0", 1024],
332
+ ["Y", "400.0", 1024],
333
+ ["M", "M0.0", 1024],
334
+ ["C", "C0", 256],
335
+ ["T", "T0", 256],
336
+ ["L", "H0.0", 1024],
337
+ ["SC", "M400.0", 1024],
338
+ ["D", "D0", 1024],
339
+ ["H", "D1024", 1024],
340
+ ["SD", "D2048", 1024],
341
+ ["PRG", "D3072", 1024] # ..D4095
342
+ ].each do |s,d,c|
343
+ h[s] = [OmronDevice.new(d), c]
344
+ end
345
+ h
346
+ end
347
+ end
348
+
349
+ def int_to_a value, size
350
+ a = []
351
+ (size - 1).downto 0 do |i|
352
+ a << ((value >> (i * 8)) & 0xff)
353
+ end
354
+ a
355
+ end
356
+
357
+ def to_int a
358
+ v = 0
359
+ a.each do |e|
360
+ v <<= 8
361
+ v += e
362
+ end
363
+ v
364
+ end
365
+
366
+ def dump_packet packet
367
+ a =
368
+ packet.map{|e|
369
+ e.to_s(16).rjust(2, '0')
370
+ }
371
+ "[#{a.join(', ')}]"
372
+ end
373
+
374
+ end
375
+
376
+ end
377
+ end
378
+ end