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.
- checksums.yaml +4 -4
- data/Gemfile +2 -0
- data/Gemfile.lock +1 -1
- data/README.md +92 -23
- data/README_jp.md +104 -32
- data/escalator.gemspec +2 -2
- data/exe/escalator +25 -0
- data/lib/escalator/asm.rb +11 -8
- data/lib/escalator/cli.rb +11 -2
- data/lib/escalator/config.rb +50 -27
- data/lib/escalator/config_target.rb +93 -0
- data/lib/escalator/console.rb +124 -0
- data/lib/escalator/escalator.rb +36 -0
- data/lib/escalator/plc_define.rb +35 -0
- data/lib/escalator/plc_device.rb +171 -0
- data/lib/escalator/protocol/emulator/emu_protocol.rb +49 -0
- data/lib/escalator/protocol/emulator/emulator.rb +29 -0
- data/lib/escalator/protocol/keyence/keyence.rb +31 -0
- data/lib/escalator/protocol/keyence/kv_device.rb +51 -0
- data/lib/escalator/protocol/keyence/kv_protocol.rb +161 -0
- data/lib/escalator/protocol/mitsubishi/mc_protocol.rb +44 -4
- data/lib/escalator/protocol/mitsubishi/mitsubishi.rb +0 -6
- data/lib/escalator/protocol/mitsubishi/qdevice.rb +2 -2
- data/lib/escalator/protocol/protocol.rb +29 -2
- data/lib/escalator/tasks/build.rb +22 -28
- data/lib/escalator/uploader.rb +16 -29
- data/lib/escalator/version.rb +1 -1
- data/lib/escalator.rb +4 -12
- data/lib/plc/emulator/emu_device.rb +121 -0
- data/lib/plc/emulator/emu_plc.rb +411 -0
- data/lib/plc/emulator/emu_plc_server.rb +71 -0
- data/lib/plc/emulator/emulator.rb +28 -0
- data/{plc → lib/plc}/mitsubishi/iq-r/r08/LICENSE +0 -0
- data/lib/plc/mitsubishi/iq-r/r08/r08.gx3 +0 -0
- data/lib/plc/plc.rb +27 -0
- data/template/escalator/asm/main.esc +52 -7
- data/template/escalator/config/plc.yml +23 -7
- metadata +22 -8
- 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.
|
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
|
132
|
-
|
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]
|
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
|
@@ -100,11 +100,11 @@ module Mitsubishi
|
|
100
100
|
end
|
101
101
|
|
102
102
|
def + value
|
103
|
-
|
103
|
+
self.class.new self.suffix, self.number + value
|
104
104
|
end
|
105
105
|
|
106
106
|
def - value
|
107
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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 "
|
84
|
+
puts "done uploading"
|
101
85
|
end
|
102
86
|
|
103
|
-
desc "
|
104
|
-
task :
|
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 =>
|
101
|
+
task :default => :console
|
data/lib/escalator/uploader.rb
CHANGED
@@ -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
|
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
|
81
|
-
break if (v &
|
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
|
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
|
90
|
-
break if (v &
|
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
|
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,
|
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
|
100
|
-
break if (v &
|
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,
|
94
|
+
@protocol.set_words_to_device chunk, EscDevice.program_area_device
|
108
95
|
end
|
109
96
|
end
|
110
97
|
|
data/lib/escalator/version.rb
CHANGED
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
|
-
|
25
|
-
|
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
|
-
|
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
|