escalator 0.2.1 → 0.2.2

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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +92 -23
  5. data/README_jp.md +104 -32
  6. data/escalator.gemspec +2 -2
  7. data/exe/escalator +25 -0
  8. data/lib/escalator/asm.rb +11 -8
  9. data/lib/escalator/cli.rb +11 -2
  10. data/lib/escalator/config.rb +50 -27
  11. data/lib/escalator/config_target.rb +93 -0
  12. data/lib/escalator/console.rb +124 -0
  13. data/lib/escalator/escalator.rb +36 -0
  14. data/lib/escalator/plc_define.rb +35 -0
  15. data/lib/escalator/plc_device.rb +171 -0
  16. data/lib/escalator/protocol/emulator/emu_protocol.rb +49 -0
  17. data/lib/escalator/protocol/emulator/emulator.rb +29 -0
  18. data/lib/escalator/protocol/keyence/keyence.rb +31 -0
  19. data/lib/escalator/protocol/keyence/kv_device.rb +51 -0
  20. data/lib/escalator/protocol/keyence/kv_protocol.rb +161 -0
  21. data/lib/escalator/protocol/mitsubishi/mc_protocol.rb +44 -4
  22. data/lib/escalator/protocol/mitsubishi/mitsubishi.rb +0 -6
  23. data/lib/escalator/protocol/mitsubishi/qdevice.rb +2 -2
  24. data/lib/escalator/protocol/protocol.rb +29 -2
  25. data/lib/escalator/tasks/build.rb +22 -28
  26. data/lib/escalator/uploader.rb +16 -29
  27. data/lib/escalator/version.rb +1 -1
  28. data/lib/escalator.rb +4 -12
  29. data/lib/plc/emulator/emu_device.rb +121 -0
  30. data/lib/plc/emulator/emu_plc.rb +411 -0
  31. data/lib/plc/emulator/emu_plc_server.rb +71 -0
  32. data/lib/plc/emulator/emulator.rb +28 -0
  33. data/{plc → lib/plc}/mitsubishi/iq-r/r08/LICENSE +0 -0
  34. data/lib/plc/mitsubishi/iq-r/r08/r08.gx3 +0 -0
  35. data/lib/plc/plc.rb +27 -0
  36. data/template/escalator/asm/main.esc +52 -7
  37. data/template/escalator/config/plc.yml +23 -7
  38. metadata +22 -8
  39. data/plc/mitsubishi/iq-r/r08/r08.gx3 +0 -0
@@ -0,0 +1,161 @@
1
+ # The MIT License (MIT)
2
+ #
3
+ # Copyright (c) 2016 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 Escalator
25
+ module Protocol
26
+ module Keyence
27
+
28
+ class KvProtocol < Protocol
29
+
30
+ def initialize options={}
31
+ super
32
+ @host = options[:host] || "192.168.0.10"
33
+ @port = options[:port] || 8501
34
+ end
35
+
36
+ def open
37
+ open!
38
+ rescue
39
+ nil
40
+ end
41
+
42
+ def open!
43
+ @socket ||= TCPSocket.open(@host, @port)
44
+ end
45
+
46
+ def close
47
+ @socket.close if @socket
48
+ @socket = nil
49
+ end
50
+
51
+ def get_bit_from_device device
52
+ device = device_by_name device
53
+ get_bits_from_device(1, device).first
54
+ end
55
+
56
+ def get_bits_from_device count, device
57
+ values = get_words_from_device count, device
58
+ values.map{|v| v == 0 ? false : true}
59
+ values.each do |v|
60
+ device.bool = v
61
+ device = device_by_name (device+1).name
62
+ end
63
+ values
64
+ end
65
+
66
+ def set_bits_to_device bits, device
67
+ device = device_by_name device
68
+ bits = [bits] unless bits.is_a? Array
69
+ @logger.debug("#{device.name}[#{bits.size}] <= #{bits}")
70
+ bits.each do |v|
71
+ cmd = "ST"
72
+ case v
73
+ when false, 0
74
+ cmd = "RS"
75
+ end
76
+ packet = "#{cmd} #{device.name}\r"
77
+ @logger.debug("> #{dump_packet packet}")
78
+ open
79
+ @socket.puts(packet)
80
+ res = receive
81
+ raise res unless /OK/i =~ res
82
+ device = device_by_name (device+1).name
83
+ end
84
+ end
85
+ alias :set_bit_to_device :set_bits_to_device
86
+
87
+
88
+ def get_word_from_device device
89
+ device = device_by_name device
90
+ get_words_from_device(1, device).first
91
+ end
92
+
93
+ def get_words_from_device(count, device)
94
+ device = device_by_name device
95
+ packet = "RDS #{device.name} #{count}\r"
96
+ @logger.debug("> #{dump_packet packet}")
97
+ open
98
+ @socket.puts(packet)
99
+ res = receive
100
+ values = res.split(/\s+/).map{|v| v.to_i}
101
+ @logger.debug("#{device.name}[#{count}] => #{values}")
102
+ values
103
+ end
104
+
105
+ def set_words_to_device words, device
106
+ device = local_device device
107
+ words = [words] unless words.is_a? Array
108
+ packet = "WRS #{device.name} #{words.size} #{words.map{|w| w.to_s}.join(" ")}\r"
109
+ @logger.debug("> #{dump_packet packet}")
110
+ open
111
+ @socket.puts(packet)
112
+ res = receive
113
+ @logger.debug("#{device.name}[#{words.size}] <= #{words}")
114
+ raise res unless /OK/i =~ res
115
+ end
116
+ alias :set_word_to_device :set_words_to_device
117
+
118
+
119
+ def device_by_name name
120
+ case name
121
+ when String
122
+ device_class.new name
123
+ else
124
+ # it may be already Device
125
+ name
126
+ end
127
+ end
128
+
129
+
130
+ def receive
131
+ res = ""
132
+ begin
133
+ Timeout.timeout(0.1) do
134
+ res = @socket.gets
135
+ end
136
+ rescue Timeout::Error
137
+ end
138
+ @logger.debug("< #{dump_packet res}")
139
+ res.chomp
140
+ end
141
+
142
+ def dump_packet packet
143
+ packet.dup.chomp
144
+ end
145
+
146
+ private
147
+
148
+ def local_device device
149
+ # TODO:
150
+ device
151
+ end
152
+
153
+ def device_class
154
+ KvDevice
155
+ end
156
+
157
+ end
158
+
159
+ end
160
+ end
161
+ end
@@ -29,11 +29,18 @@ module Mitsubishi
29
29
 
30
30
  def initialize options={}
31
31
  super
32
- @host = options[:host] || "192.168.0.1"
32
+ @host = options[:host] || "192.168.0.10"
33
33
  @port = options[:port] || 5010
34
+ prepare_device_map
34
35
  end
35
36
 
36
37
  def open
38
+ open!
39
+ rescue
40
+ nil
41
+ end
42
+
43
+ def open!
37
44
  @socket ||= TCPSocket.open(@host, @port)
38
45
  end
39
46
 
@@ -117,6 +124,8 @@ module Mitsubishi
117
124
  case name
118
125
  when String
119
126
  QDevice.new name
127
+ when EscDevice
128
+ local_device_of name
120
129
  else
121
130
  # it may be already QDevice
122
131
  name
@@ -128,17 +137,18 @@ module Mitsubishi
128
137
  res = []
129
138
  len = 0
130
139
  begin
131
- Timeout.timeout(0.1) do
132
- while true
140
+ Timeout.timeout(1.0) do
141
+ loop do
133
142
  c = @socket.read(1)
134
143
  next if c.nil? || c == ""
135
144
 
136
145
  res << c.bytes.first
137
- len = res[7] + res[8] << 8 if res.length >= 9
146
+ len = res[7,2].pack("c*").unpack("v*").first if res.length >= 9
138
147
  break if (len + 9 == res.length)
139
148
  end
140
149
  end
141
150
  rescue Timeout::Error
151
+ puts "*** ERROR: TIME OUT ***"
142
152
  end
143
153
  @logger.debug("< #{dump_packet res}")
144
154
  res
@@ -221,6 +231,36 @@ module Mitsubishi
221
231
  "[" + a.join(", ") + "]"
222
232
  end
223
233
 
234
+ def prepare_device_map
235
+ @conv_dev_dict ||= begin
236
+ h = {}
237
+ [
238
+ ["X", "X0", 1024],
239
+ ["Y", "Y0", 1024],
240
+ ["M", "M0", 1024],
241
+ ["C", "C0", 256],
242
+ ["T", "T0", 256],
243
+ ["L", "L0", 1024],
244
+ ["SC", "M1024", 1024],
245
+ ["D", "D0", 1024],
246
+ ["H", "D1024", 1024],
247
+ ["SD", "D2048", 1024],
248
+ ["PRG", "D3072", 1024] # ..D4095
249
+ ].each do |s,d,c|
250
+ h[s] = [QDevice.new(d), c]
251
+ end
252
+ h
253
+ end
254
+ end
255
+
256
+ def local_device_of device
257
+ return device if device.is_a? QDevice
258
+ d, c = @conv_dev_dict[device.suffix]
259
+ return nil unless device.number < c
260
+ ld = QDevice.new(d.suffix, d.number + device.number)
261
+ device_by_name ld.name
262
+ end
263
+
224
264
  end
225
265
 
226
266
  end
@@ -23,14 +23,8 @@
23
23
 
24
24
  $:.unshift File.dirname(__FILE__)
25
25
 
26
- module Escalator
27
- module Protocol
28
-
29
26
  require 'socket'
30
27
  require 'logger'
31
28
  require 'timeout'
32
29
  require 'qdevice'
33
30
  require 'mc_protocol'
34
-
35
- end
36
- end
@@ -100,11 +100,11 @@ module Mitsubishi
100
100
  end
101
101
 
102
102
  def + value
103
- QDevice.new self.suffix, self.number + value
103
+ self.class.new self.suffix, self.number + value
104
104
  end
105
105
 
106
106
  def - value
107
- QDevice.new self.suffix, [self.number - value, 0].max
107
+ self.class.new self.suffix, [self.number - value, 0].max
108
108
  end
109
109
 
110
110
  end
@@ -21,7 +21,13 @@
21
21
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
22
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
23
 
24
- $:.unshift File.dirname(__FILE__)
24
+ dir = File.expand_path(File.dirname(__FILE__))
25
+ $:.unshift dir unless $:.include? dir
26
+
27
+ module Escalator
28
+ module Protocol
29
+ end
30
+ end
25
31
 
26
32
  module Escalator
27
33
  module Protocol
@@ -32,7 +38,7 @@ module Protocol
32
38
 
33
39
  def initialize options={}
34
40
  @logger = Logger.new(STDOUT)
35
- @logger.level = options[:log_level] || Logger::INFO
41
+ self.log_level = options[:log_level] || :info
36
42
  end
37
43
 
38
44
  def log_level= level
@@ -70,9 +76,30 @@ module Protocol
70
76
 
71
77
  def device_by_name name; nil; end
72
78
 
79
+ def get_from_devices device, count = 1
80
+ d = device_by_name device
81
+ if d.bit_device?
82
+ get_bits_from_device count, d
83
+ else
84
+ get_words_from_device count, d
85
+ end
86
+ end
87
+
88
+ def set_to_devices device, values
89
+ d = device_by_name device
90
+ if d.bit_device?
91
+ set_bits_to_device values, d
92
+ else
93
+ set_words_to_device values, d
94
+ end
95
+ end
96
+
73
97
  end
74
98
 
75
99
  end
76
100
  end
77
101
 
102
+ require 'keyence/keyence'
103
+ # Use load instead require, because there are two emulator files.
104
+ load File.join(dir, 'emulator/emulator.rb')
78
105
  require 'mitsubishi/mitsubishi'
@@ -21,15 +21,12 @@
21
21
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
22
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
23
 
24
- Escalator_root = File.expand_path(File.join(File.dirname(__FILE__), "../../../"))
24
+ dir = File.expand_path(File.join(File.dirname(__FILE__), "../../../lib"))
25
+ $:.unshift dir unless $:.include? dir
25
26
 
26
- require "escalator/config"
27
- require 'escalator/asm'
28
- require 'escalator/intel_hex'
29
27
  require 'rake/loaders/makefile'
30
28
  require 'fileutils'
31
-
32
- #directory "build"
29
+ require "escalator"
33
30
 
34
31
  directory "build"
35
32
 
@@ -69,23 +66,6 @@ rule %r{^build/.+\.hex} => ['%{^build,asm}X.esc'] do |t|
69
66
  File.write(t.name, hex.dump)
70
67
  end
71
68
 
72
- desc "GX Works Memory Image"
73
- rule %r{^build/.+\.gxwm} => ['%{^build,asm}X.esc'] do |t|
74
- Rake::Task["build"].invoke
75
- begin
76
- $stderr = File.open('hb.log', 'w')
77
- $stdout = $stderr
78
- ensure
79
- $stdout = STDOUT
80
- $stderr = STDERR
81
- end
82
- stream = StringIO.new
83
- puts "gxwm #{t.source}"
84
- asm = Escalator::Asm.new File.read(t.source)
85
- hex = Escalator::IntelHex.new asm.codes
86
- File.write(t.name, hex.gxworks_memory_image)
87
- end
88
-
89
69
  desc "Clean all generated files."
90
70
  task :clean do
91
71
  FileUtils.rm_r "build"
@@ -93,15 +73,29 @@ end
93
73
 
94
74
  @config = Escalator::EscalatorConfig.default
95
75
 
76
+ desc "Install program to PLCs."
96
77
  task :upload => @config.output do
97
- u = @config.uploader
78
+ t = @config.target ENV['target']
79
+ t.run
80
+ u = t.uploader
98
81
  u.source = @config.output
82
+ puts "uploading #{u.source} ..."
99
83
  u.upload
100
- puts "upload #{u.source}"
84
+ puts "done uploading"
101
85
  end
102
86
 
103
- desc "Install program to PLCs."
104
- task :plc => :upload do
87
+ desc "Launch emulator."
88
+ task :emulator do
89
+ t = @config.target :emulator
90
+ t.run
91
+ puts "launch emulator"
92
+ Escalator::Console.instance.run
93
+ end
94
+
95
+ task :console => :upload do
96
+ c = Escalator::Console.instance
97
+ c.target = @config.target ENV['target']
98
+ c.run
105
99
  end
106
100
 
107
- task :default => %w(build/main.lst build/main.hex build/main.gxwm)
101
+ task :default => :console
@@ -21,27 +21,22 @@
21
21
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
22
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
23
 
24
+ require "escalator/plc_define"
25
+ require "escalator/plc_device"
26
+
24
27
  module Escalator
25
28
 
26
29
  class Uploader
30
+ include ::Escalator::PlcDefine
27
31
 
28
32
  attr_accessor :protocol
29
- attr_accessor :program_area, :interaction_area
30
33
  attr_accessor :source
31
34
  attr_accessor :data
32
35
 
33
36
  def initialize options={}
34
37
  @protocol = options[:protocol] if options[:protocol]
35
- @program_area = options[:program_area] if options[:program_area]
36
- @interaction_area = options[:interaction_area] if options[:interaction_area]
37
38
  end
38
39
 
39
- STOP_PLC_FLAG = 2 # bit 1
40
- CLEAR_PROGRAM_FLAG = 4 # bit 2 require bit 1 on
41
-
42
- CYCLE_RUN_FLAG = 2
43
-
44
-
45
40
  def upload
46
41
  # stop plc
47
42
  stop_plc
@@ -59,52 +54,44 @@ module Escalator
59
54
 
60
55
  def word_data
61
56
  data.each_slice(2).map do |pair|
57
+ pair << 0 if pair.size == 1
62
58
  pair.pack("c*").unpack("n*")
63
59
  end.flatten
64
60
  end
65
61
 
66
62
  private
67
63
 
68
- def to_plc_status
69
- @to_plc_status ||= interaction_area
70
- end
71
-
72
- def from_plc_status
73
- @from_plc_status ||= interaction_area.next_device
74
- end
75
-
76
-
77
64
  def stop_plc
78
- @protocol.set_word_to_device STOP_PLC_FLAG, to_plc_status
65
+ @protocol.set_word_to_device ESC_STATUS_TO_PLC_STOP_PLC_FLAG, EscDevice.status_to_plc_device
79
66
  Timeout.timeout(5) do
80
- v = @protocol.get_word_from_device from_plc_status
81
- break if (v & STOP_PLC_FLAG) != 0
67
+ v = @protocol.get_word_from_device EscDevice.status_from_plc_device
68
+ break if (v & ESC_STATUS_TO_PLC_STOP_PLC_FLAG) != 0
82
69
  sleep 0.1
83
70
  end
84
71
  end
85
72
 
86
73
  def clear_program
87
- @protocol.set_word_to_device STOP_PLC_FLAG | CLEAR_PROGRAM_FLAG, to_plc_status
74
+ @protocol.set_word_to_device ESC_STATUS_TO_PLC_STOP_PLC_FLAG | ESC_STATUS_TO_PLC_CLEAR_PROGRAM, EscDevice.status_to_plc_device
88
75
  Timeout.timeout(5) do
89
- v = @protocol.get_word_from_device from_plc_status
90
- break if (v & CLEAR_PROGRAM_FLAG) != 0
76
+ v = @protocol.get_word_from_device EscDevice.status_from_plc_device
77
+ break if (v & ESC_STATUS_TO_PLC_CLEAR_PROGRAM) != 0
91
78
  sleep 0.1
92
79
  end
93
- @protocol.set_word_to_device STOP_PLC_FLAG, to_plc_status
80
+ @protocol.set_word_to_device ESC_STATUS_TO_PLC_STOP_PLC_FLAG, EscDevice.status_to_plc_device
94
81
  end
95
82
 
96
83
  def run_plc
97
- @protocol.set_word_to_device 0, to_plc_status
84
+ @protocol.set_word_to_device 0, EscDevice.status_to_plc_device
98
85
  Timeout.timeout(5) do
99
- v = @protocol.get_word_from_device from_plc_status
100
- break if (v & CYCLE_RUN_FLAG) != 0
86
+ v = @protocol.get_word_from_device EscDevice.status_from_plc_device
87
+ break if (v & ESC_STATUS_FROM_PLC_CYCLE_RUN) != 0
101
88
  sleep 0.1
102
89
  end
103
90
  end
104
91
 
105
92
  def write_program
106
93
  word_data.each_slice(2*1024) do |chunk|
107
- @protocol.set_words_to_device chunk, @program_area
94
+ @protocol.set_words_to_device chunk, EscDevice.program_area_device
108
95
  end
109
96
  end
110
97
 
@@ -22,5 +22,5 @@
22
22
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
23
 
24
24
  module Escalator
25
- VERSION = "0.2.1"
25
+ VERSION = "0.2.2"
26
26
  end
data/lib/escalator.rb CHANGED
@@ -21,16 +21,8 @@
21
21
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
22
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
23
 
24
- require "escalator/version"
25
- require "escalator/cli"
26
- require "escalator/asm"
27
- require "escalator/intel_hex"
28
- require "escalator/config"
24
+ dir = File.expand_path(File.dirname(__FILE__))
25
+ $:.unshift dir unless $:.include? dir
29
26
 
30
- Escalator_root = File.expand_path(File.join(File.dirname(__FILE__), ".."))
31
-
32
- module Escalator
33
- end
34
-
35
-
36
- Escalator::CLI.start
27
+ require "plc/plc"
28
+ require "escalator/escalator"
@@ -0,0 +1,121 @@
1
+ #
2
+ # Copyright (c) 2016 ITO SOFT DESIGN Inc.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ require 'escalator/plc_device'
24
+
25
+ include Escalator
26
+
27
+ module Plc
28
+ module Emulator
29
+
30
+ class EmuDevice < PlcDevice
31
+
32
+ attr_reader :in_value, :out_value
33
+
34
+ def initialize a, b = nil
35
+ super
36
+ @lock = Mutex.new
37
+ @in_value = 0
38
+ @out_value = 0
39
+ end
40
+
41
+ def reset
42
+ @lock.synchronize {
43
+ super
44
+ @in_value = nil
45
+ @out_value = 0
46
+ }
47
+ end
48
+
49
+ def value= value
50
+ set_value value
51
+ end
52
+
53
+ def bool kind=nil
54
+ v = value kind
55
+ case v
56
+ when nil, false, 0
57
+ false
58
+ else
59
+ true
60
+ end
61
+ end
62
+
63
+ def bool= value
64
+ @lock.synchronize { super }
65
+ end
66
+
67
+ def word kind=nil
68
+ value kind
69
+ end
70
+
71
+ def word= value
72
+ @lock.synchronize {
73
+ super
74
+ }
75
+ end
76
+
77
+ def value kind=nil
78
+ @lock.synchronize {
79
+ case kind
80
+ when :in
81
+ @in_value
82
+ when :out
83
+ @out_value
84
+ else
85
+ @value
86
+ end
87
+ }
88
+ end
89
+
90
+ def set_value value, kind=nil
91
+ @lock.synchronize {
92
+ case kind
93
+ when :in
94
+ @in_value = value
95
+ when :out
96
+ @out_value = value
97
+ else
98
+ @value = value
99
+ end
100
+ }
101
+ end
102
+
103
+ def sync_input
104
+ @lock.synchronize {
105
+ unless @in_value.nil?
106
+ @value = @in_value
107
+ @in_value = nil
108
+ end
109
+ }
110
+ end
111
+
112
+ def sync_output
113
+ @lock.synchronize {
114
+ @out_value = @value
115
+ }
116
+ end
117
+
118
+ end
119
+
120
+ end
121
+ end