ladder_drive 0.5.2 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +0 -1
- data/Gemfile +6 -2
- data/Gemfile.lock +93 -17
- data/README.md +47 -14
- data/README_jp.md +51 -2
- data/Rakefile +31 -0
- data/doc/jp/raspberrypi.md +183 -0
- data/ladder_drive.gemspec +2 -1
- data/lib/ladder_drive/asm.rb +62 -7
- data/lib/ladder_drive/cli.rb +11 -0
- data/lib/ladder_drive/plc_device.rb +33 -4
- data/lib/ladder_drive/protocol/mitsubishi/fx_device.rb +14 -0
- data/lib/ladder_drive/protocol/mitsubishi/fx_protocol.rb +330 -0
- data/lib/ladder_drive/protocol/mitsubishi/mc_protocol.rb +6 -6
- data/lib/ladder_drive/protocol/mitsubishi/mitsubishi.rb +4 -0
- data/lib/ladder_drive/protocol/protocol.rb +1 -0
- data/lib/ladder_drive/version.rb +1 -1
- data/lib/plc/emulator/emu_device.rb +7 -0
- data/lib/plc/emulator/emu_plc.rb +6 -2
- data/lib/plc/emulator/emu_plc_server.rb +9 -8
- data/lib/plc/emulator/plc_plugins.rb +155 -0
- data/plugins/blank_plugin.rb +11 -0
- data/plugins/google_drive_plugin.rb +191 -0
- data/plugins/ifttt_plugin.rb +134 -0
- data/plugins/plc_mapper_plugin.rb +186 -0
- data/plugins/slack_plugin.rb +155 -0
- data/plugins/trello_plugin.rb +156 -0
- metadata +41 -5
data/lib/ladder_drive/asm.rb
CHANGED
@@ -34,10 +34,17 @@ module LadderDrive
|
|
34
34
|
def initialize source, endian = nil
|
35
35
|
@endian = endian || BIG_ENDIAN
|
36
36
|
@lines = []
|
37
|
+
line_no = 1
|
37
38
|
address = 0
|
38
39
|
source.each_line do |line|
|
39
|
-
|
40
|
-
|
40
|
+
begin
|
41
|
+
@lines << AsmLine.new(line, address, @endian)
|
42
|
+
address = @lines.last.next_address
|
43
|
+
line_no += 1
|
44
|
+
rescue SyntaxError => e
|
45
|
+
puts "#{e.class}: line:#{line_no}; #{line.chomp}; #{e.to_s} "
|
46
|
+
throw
|
47
|
+
end
|
41
48
|
end
|
42
49
|
end
|
43
50
|
|
@@ -113,7 +120,7 @@ module LadderDrive
|
|
113
120
|
@codes << encode_mnemonic(mnemonic)
|
114
121
|
case operand_type(mnemonic)
|
115
122
|
when OPERAND_TYPE_TYPE_AND_NUMBER
|
116
|
-
@codes += parse_type_and_number(operand1)
|
123
|
+
@codes += parse_type_and_number(mnemonic, operand1, operand2)
|
117
124
|
end
|
118
125
|
end
|
119
126
|
end
|
@@ -166,7 +173,7 @@ EOB
|
|
166
173
|
mnemonic_dict[mnemonic]
|
167
174
|
end
|
168
175
|
|
169
|
-
def parse_type_and_number operand
|
176
|
+
def parse_type_and_number mnemonic, operand, value
|
170
177
|
/([[:alpha:]]*)(\d+[0-9A-Fa-f]*)\.?(\d*)?/ =~ operand
|
171
178
|
suffix = $1
|
172
179
|
number = $2
|
@@ -189,15 +196,63 @@ EOB
|
|
189
196
|
end
|
190
197
|
|
191
198
|
if number < 256
|
192
|
-
[type_code, number]
|
199
|
+
codes = [type_code, number]
|
193
200
|
else
|
194
201
|
case endian
|
195
202
|
when Asm::LITTLE_ENDIAN
|
196
|
-
[type_code | 0x10, number & 0xff, (number & 0xff00) >> 8]
|
203
|
+
codes = [type_code | 0x10, number & 0xff, (number & 0xff00) >> 8]
|
197
204
|
when Asm::BIG_ENDIAN
|
198
|
-
[type_code | 0x10, (number & 0xff00) >> 8, number & 0xff]
|
205
|
+
codes = [type_code | 0x10, (number & 0xff00) >> 8, number & 0xff]
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# If mnemonic is out/outi and device type is timer or counter, append time or count.
|
210
|
+
case mnemonic
|
211
|
+
when /OUT/
|
212
|
+
case suffix
|
213
|
+
when 'T'
|
214
|
+
codes += time_value(value)
|
215
|
+
when 'C'
|
216
|
+
codes += counter_value(value)
|
199
217
|
end
|
200
218
|
end
|
219
|
+
|
220
|
+
codes
|
221
|
+
end
|
222
|
+
|
223
|
+
def time_value value
|
224
|
+
raise SyntaxError.new "It must be required time value." unless /^\d*\.?\d*$/ =~ value
|
225
|
+
|
226
|
+
t = value.to_f
|
227
|
+
g = 0
|
228
|
+
codes = []
|
229
|
+
while t < 16384 && g <= 3
|
230
|
+
t *= 10.0
|
231
|
+
g += 1
|
232
|
+
end
|
233
|
+
t /= 10.0
|
234
|
+
g -= 1
|
235
|
+
v = (g << 14) | t.to_i
|
236
|
+
|
237
|
+
case endian
|
238
|
+
when Asm::LITTLE_ENDIAN
|
239
|
+
codes = [v & 0xff, (v & 0xff00) >> 8]
|
240
|
+
when Asm::BIG_ENDIAN
|
241
|
+
codes = [(v & 0xff00) >> 8, v & 0xff]
|
242
|
+
end
|
243
|
+
codes #rdlsf@de
|
244
|
+
end
|
245
|
+
|
246
|
+
def counter_value value
|
247
|
+
raise SyntaxError.new "It must be required count value." unless /^\d+$/ =~ value
|
248
|
+
|
249
|
+
v = value.to_i
|
250
|
+
case endian
|
251
|
+
when Asm::LITTLE_ENDIAN
|
252
|
+
codes = [v & 0xff, (v & 0xff00) >> 8]
|
253
|
+
when Asm::BIG_ENDIAN
|
254
|
+
codes = [(v & 0xff00) >> 8, v & 0xff]
|
255
|
+
end
|
201
256
|
end
|
202
257
|
|
203
258
|
end
|
data/lib/ladder_drive/cli.rb
CHANGED
@@ -51,5 +51,16 @@ module LadderDrive
|
|
51
51
|
puts "#{name} was successfully created."
|
52
52
|
end
|
53
53
|
|
54
|
+
desc "plugin", "Install the specified plugin."
|
55
|
+
def plugin(name)
|
56
|
+
root_dir = File.expand_path(File.join(File.dirname(__FILE__), "..", ".."))
|
57
|
+
plugins_path = File.join(root_dir, "plugins")
|
58
|
+
path = File.join(plugins_path, "#{name}_plugin.rb")
|
59
|
+
if File.exist? path
|
60
|
+
mkdir_p "plugins"
|
61
|
+
cp path, "plugins/#{name}_plugins.rb"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
54
65
|
end
|
55
66
|
end
|
@@ -99,9 +99,14 @@ module LadderDrive
|
|
99
99
|
end
|
100
100
|
end
|
101
101
|
|
102
|
+
# NOTE: override at subclass
|
103
|
+
# It should get from plc
|
104
|
+
def device_by_suffix_number suffix, number
|
105
|
+
self.class.new suffix, number
|
106
|
+
end
|
107
|
+
|
102
108
|
def next_device
|
103
|
-
|
104
|
-
d
|
109
|
+
device_by_suffix_number @suffix, @number + 1
|
105
110
|
end
|
106
111
|
|
107
112
|
def bit_device?
|
@@ -109,11 +114,11 @@ module LadderDrive
|
|
109
114
|
end
|
110
115
|
|
111
116
|
def + value
|
112
|
-
|
117
|
+
device_by_suffix_number self.suffix, self.number + value
|
113
118
|
end
|
114
119
|
|
115
120
|
def - value
|
116
|
-
|
121
|
+
device_by_suffix_number self.suffix, [self.number - value, 0].max
|
117
122
|
end
|
118
123
|
|
119
124
|
def input?
|
@@ -132,6 +137,30 @@ module LadderDrive
|
|
132
137
|
alias :word :value
|
133
138
|
alias :word= :value=
|
134
139
|
|
140
|
+
def text len=8
|
141
|
+
n = (len + 1) / 2
|
142
|
+
d = self
|
143
|
+
a = []
|
144
|
+
n.times{ a << d.value; d = d.next_device}
|
145
|
+
s = a.pack("n*").split("\x00").first
|
146
|
+
s ? s[0,len] : ""
|
147
|
+
end
|
148
|
+
|
149
|
+
def set_text value, len=8
|
150
|
+
value = value[0, len]
|
151
|
+
value << "\00" unless value.length % 2 == 0
|
152
|
+
a = value.unpack("n*")
|
153
|
+
d = self
|
154
|
+
a.each do |v|
|
155
|
+
d.value = v
|
156
|
+
d = d.next_device
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def text= value
|
161
|
+
set_text value
|
162
|
+
end
|
163
|
+
|
135
164
|
def device_code
|
136
165
|
ESC_SUFFIXES.index @suffix
|
137
166
|
end
|
@@ -0,0 +1,330 @@
|
|
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 LadderDrive
|
25
|
+
module Protocol
|
26
|
+
module Mitsubishi
|
27
|
+
|
28
|
+
class FxProtocol < Protocol
|
29
|
+
|
30
|
+
attr_accessor :pc_no
|
31
|
+
attr_accessor :baudrate
|
32
|
+
attr_accessor :station_no
|
33
|
+
attr_accessor :wait_time
|
34
|
+
|
35
|
+
STX = "\u0002"
|
36
|
+
ETX = "\u0003"
|
37
|
+
EOT = "\u0004"
|
38
|
+
ENQ = "\u0005"
|
39
|
+
ACK = "\u0006"
|
40
|
+
LF = "\u000a"
|
41
|
+
CR = "\u000d"
|
42
|
+
NAK = "\u0015"
|
43
|
+
|
44
|
+
DELIMITER = "\r\n"
|
45
|
+
TIMEOUT = 1.0
|
46
|
+
|
47
|
+
def initialize options={}
|
48
|
+
super
|
49
|
+
@port = options[:port] || `ls /dev/tty.usb*`.split("\n").map{|l| l.chomp}.first
|
50
|
+
@pc_no = 0xff
|
51
|
+
@baudrate = 19200
|
52
|
+
@station_no = 0
|
53
|
+
@wait_time = 0
|
54
|
+
#prepare_device_map
|
55
|
+
end
|
56
|
+
|
57
|
+
def open
|
58
|
+
open!
|
59
|
+
rescue
|
60
|
+
nil
|
61
|
+
end
|
62
|
+
|
63
|
+
def open!
|
64
|
+
return false unless @port
|
65
|
+
begin
|
66
|
+
# port, baudrate, bits, stop bits, parity(0:none, 1:even, 2:odd)
|
67
|
+
@comm ||= SerialPort.new(@port, @baudrate, 7, 1, 2).tap do |s|
|
68
|
+
s.read_timeout = (TIMEOUT * 1000.0).to_i
|
69
|
+
end
|
70
|
+
rescue => e
|
71
|
+
p e
|
72
|
+
nil
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def close
|
77
|
+
@comm.close if @comm
|
78
|
+
@comm = nil
|
79
|
+
end
|
80
|
+
|
81
|
+
def get_bit_from_device device
|
82
|
+
device = device_by_name device
|
83
|
+
get_bits_from_device(1, device).first
|
84
|
+
end
|
85
|
+
|
86
|
+
def get_bits_from_device count, device
|
87
|
+
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
|
88
|
+
|
89
|
+
device = device_by_name device
|
90
|
+
packet = body_for_get_bits_from_device(count, device) + DELIMITER
|
91
|
+
@logger.debug("> #{dump_packet packet}")
|
92
|
+
open
|
93
|
+
@comm.write(packet)
|
94
|
+
@comm.flush
|
95
|
+
res = receive
|
96
|
+
bits = []
|
97
|
+
|
98
|
+
if res
|
99
|
+
if check_sum(res[0..-5]) == res[-4,2]
|
100
|
+
bits =
|
101
|
+
res[5..-6].each_char.map do |c|
|
102
|
+
c == "1" ? true : false
|
103
|
+
end
|
104
|
+
else
|
105
|
+
end
|
106
|
+
end
|
107
|
+
@logger.debug("> #{dump_packet ack_packet}")
|
108
|
+
@comm.write(ack_packet)
|
109
|
+
@logger.debug("get #{device.name} => #{bits}")
|
110
|
+
|
111
|
+
bits
|
112
|
+
end
|
113
|
+
|
114
|
+
def set_bits_to_device bits, device
|
115
|
+
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? bits.size
|
116
|
+
|
117
|
+
device = device_by_name device
|
118
|
+
packet = body_for_set_bits_to_device(bits, device)
|
119
|
+
@logger.debug("> #{dump_packet packet}")
|
120
|
+
open
|
121
|
+
@comm.write(packet)
|
122
|
+
@comm.flush
|
123
|
+
res = receive
|
124
|
+
@logger.debug("set #{bits} to:#{device.name}")
|
125
|
+
|
126
|
+
# error checking
|
127
|
+
unless res == ack_packet
|
128
|
+
raise "ERROR: return #{res} for set_bits_to_device(#{bits}, #{device.name})"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def get_word_from_device device
|
133
|
+
device = device_by_name device
|
134
|
+
get_words_from_device(1, device).first
|
135
|
+
end
|
136
|
+
|
137
|
+
def get_words_from_device(count, device)
|
138
|
+
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
|
139
|
+
|
140
|
+
device = device_by_name device
|
141
|
+
packet = body_for_get_words_from_device(count, device) + DELIMITER
|
142
|
+
@logger.debug("> #{dump_packet packet}")
|
143
|
+
open
|
144
|
+
@comm.write(packet)
|
145
|
+
@comm.flush
|
146
|
+
res = receive
|
147
|
+
words = []
|
148
|
+
|
149
|
+
if res
|
150
|
+
if check_sum(res[0..-5]) == res[-4,2]
|
151
|
+
words =
|
152
|
+
res[5..-6].scan(/.{4}/).map do |v|
|
153
|
+
v.to_i(16)
|
154
|
+
end
|
155
|
+
else
|
156
|
+
end
|
157
|
+
end
|
158
|
+
@logger.debug("> #{dump_packet ack_packet}")
|
159
|
+
@comm.write(ack_packet)
|
160
|
+
@logger.debug("get #{device.name} => #{words}")
|
161
|
+
|
162
|
+
words
|
163
|
+
end
|
164
|
+
|
165
|
+
def set_words_to_device words, device
|
166
|
+
raise ArgumentError.new("A count of words #{words.size} must be between #{available_words_range.first} and #{available_words_range.last} for #{__method__}") unless available_bits_range.include? words.size
|
167
|
+
|
168
|
+
device = device_by_name device
|
169
|
+
packet = body_for_set_words_to_device(words, device)
|
170
|
+
@logger.debug("> #{dump_packet packet}")
|
171
|
+
open
|
172
|
+
@comm.write(packet)
|
173
|
+
@comm.flush
|
174
|
+
res = receive
|
175
|
+
@logger.debug("set #{words} to: #{device.name}")
|
176
|
+
|
177
|
+
# error checking
|
178
|
+
unless res == ack_packet
|
179
|
+
raise "ERROR: return #{res} for set_bits_to_device(#{words}, #{device.name})"
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def device_by_name name
|
184
|
+
case name
|
185
|
+
when String
|
186
|
+
FxDevice.new name
|
187
|
+
when EscDevice
|
188
|
+
local_device_of name
|
189
|
+
else
|
190
|
+
# it may be already QDevice
|
191
|
+
name
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
|
196
|
+
def receive
|
197
|
+
res = ""
|
198
|
+
begin
|
199
|
+
Timeout.timeout(TIMEOUT) do
|
200
|
+
res = @comm.gets
|
201
|
+
end
|
202
|
+
res
|
203
|
+
rescue Timeout::Error
|
204
|
+
puts "*** ERROR: TIME OUT : #{res} ***"
|
205
|
+
end
|
206
|
+
@logger.debug("< #{dump_packet res}")
|
207
|
+
res
|
208
|
+
end
|
209
|
+
|
210
|
+
def available_bits_range device=nil
|
211
|
+
1..256
|
212
|
+
end
|
213
|
+
|
214
|
+
def available_words_range device=nil
|
215
|
+
1..64
|
216
|
+
end
|
217
|
+
|
218
|
+
def body_for_get_bit_from_deivce device
|
219
|
+
body_for_get_bits_from_device 1, device
|
220
|
+
end
|
221
|
+
|
222
|
+
def body_for_get_bits_from_device count, device
|
223
|
+
body = header_with_command "BR"
|
224
|
+
body += "#{device.name}#{count.to_s(16).rjust(2, '0')}"
|
225
|
+
body += check_sum(body)
|
226
|
+
body += DELIMITER
|
227
|
+
body.upcase
|
228
|
+
end
|
229
|
+
|
230
|
+
def body_for_get_words_from_device count, device
|
231
|
+
body = header_with_command "WR"
|
232
|
+
body += "#{device.name}#{count.to_s(16).rjust(2, '0')}"
|
233
|
+
body += check_sum(body)
|
234
|
+
body += DELIMITER
|
235
|
+
body.upcase
|
236
|
+
end
|
237
|
+
|
238
|
+
def body_for_set_bits_to_device bits, device
|
239
|
+
body = header_with_command "BW"
|
240
|
+
body += "#{device.name}#{bits.count.to_s(16).rjust(2, '0')}"
|
241
|
+
body += bits.map{|b| b ? "1" : "0"}.join("")
|
242
|
+
body += check_sum(body)
|
243
|
+
body += DELIMITER
|
244
|
+
body.upcase
|
245
|
+
end
|
246
|
+
alias :body_for_set_bit_to_device :body_for_set_bits_to_device
|
247
|
+
|
248
|
+
def body_for_set_words_to_device words, device
|
249
|
+
body = header_with_command "WW"
|
250
|
+
body += "#{device.name}#{words.count.to_s(16).rjust(2, '0')}"
|
251
|
+
body += words.map{|w| w.to_s(16).rjust(4, "0")}.join("")
|
252
|
+
body += check_sum(body)
|
253
|
+
body += DELIMITER
|
254
|
+
body.upcase
|
255
|
+
end
|
256
|
+
|
257
|
+
|
258
|
+
=begin
|
259
|
+
def data_for_device device
|
260
|
+
a = data_for_int device.number
|
261
|
+
a[3] = device.suffix_code
|
262
|
+
a
|
263
|
+
end
|
264
|
+
|
265
|
+
def data_for_short value
|
266
|
+
[value].pack("v").unpack("C*")
|
267
|
+
end
|
268
|
+
|
269
|
+
def data_for_int value
|
270
|
+
[value].pack("V").unpack("C*")
|
271
|
+
end
|
272
|
+
=end
|
273
|
+
|
274
|
+
def dump_packet packet
|
275
|
+
packet.inspect
|
276
|
+
end
|
277
|
+
|
278
|
+
=begin
|
279
|
+
def prepare_device_map
|
280
|
+
@conv_dev_dict ||= begin
|
281
|
+
h = {}
|
282
|
+
[
|
283
|
+
["X", "X0", 1024],
|
284
|
+
["Y", "Y0", 1024],
|
285
|
+
["M", "M0", 1024],
|
286
|
+
["C", "C0", 256],
|
287
|
+
["T", "T0", 256],
|
288
|
+
["L", "L0", 1024],
|
289
|
+
["SC", "M1024", 1024],
|
290
|
+
["D", "D0", 1024],
|
291
|
+
["H", "D1024", 1024],
|
292
|
+
["SD", "D2048", 1024],
|
293
|
+
["PRG", "D3072", 1024] # ..D4095
|
294
|
+
].each do |s,d,c|
|
295
|
+
h[s] = [QDevice.new(d), c]
|
296
|
+
end
|
297
|
+
h
|
298
|
+
end
|
299
|
+
end
|
300
|
+
=end
|
301
|
+
|
302
|
+
=begin
|
303
|
+
def local_device_of device
|
304
|
+
return device if device.is_a? QDevice
|
305
|
+
d, c = @conv_dev_dict[device.suffix]
|
306
|
+
return nil unless device.number < c
|
307
|
+
ld = QDevice.new(d.suffix, d.number + device.number)
|
308
|
+
device_by_name ld.name
|
309
|
+
end
|
310
|
+
=end
|
311
|
+
|
312
|
+
private
|
313
|
+
|
314
|
+
def check_sum packet
|
315
|
+
packet[1..-1].upcase.unpack("C*").inject(0){|s,c| s + c}.to_s(16).rjust(2, "0")[-2, 2].upcase
|
316
|
+
end
|
317
|
+
|
318
|
+
def header_with_command command
|
319
|
+
"#{ENQ}#{station_no.to_s.rjust(2,'0')}#{pc_no.to_s(16).rjust(2, '0')}#{command}#{wait_time.to_s}".upcase
|
320
|
+
end
|
321
|
+
|
322
|
+
def ack_packet
|
323
|
+
"#{ACK}#{station_no.to_s.rjust(2,'0')}#{pc_no.to_s(16).rjust(2, '0')}#{DELIMITER}".upcase
|
324
|
+
end
|
325
|
+
|
326
|
+
end
|
327
|
+
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|
@@ -58,7 +58,7 @@ module Mitsubishi
|
|
58
58
|
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
|
59
59
|
|
60
60
|
device = device_by_name device
|
61
|
-
packet = make_packet(
|
61
|
+
packet = make_packet(body_for_get_bits_from_device(count, device))
|
62
62
|
@logger.debug("> #{dump_packet packet}")
|
63
63
|
open
|
64
64
|
@socket.write(packet.pack("C*"))
|
@@ -116,7 +116,7 @@ module Mitsubishi
|
|
116
116
|
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
|
117
117
|
|
118
118
|
device = device_by_name device
|
119
|
-
packet = make_packet(
|
119
|
+
packet = make_packet(body_for_get_words_from_device(count, device))
|
120
120
|
@logger.debug("> #{dump_packet packet}")
|
121
121
|
open
|
122
122
|
@socket.write(packet.pack("C*"))
|
@@ -211,14 +211,14 @@ module Mitsubishi
|
|
211
211
|
end
|
212
212
|
|
213
213
|
def body_for_get_bit_from_deivce device
|
214
|
-
|
214
|
+
body_for_get_bits_from_device 1, device
|
215
215
|
end
|
216
216
|
|
217
|
-
def
|
218
|
-
|
217
|
+
def body_for_get_bits_from_device count, device
|
218
|
+
body_for_get_words_from_device count, device, false
|
219
219
|
end
|
220
220
|
|
221
|
-
def
|
221
|
+
def body_for_get_words_from_device count, device, word = true
|
222
222
|
body = [0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00]
|
223
223
|
body[2] = 1 unless word
|
224
224
|
body[4..7] = data_for_device(device)
|
data/lib/ladder_drive/version.rb
CHANGED
data/lib/plc/emulator/emu_plc.rb
CHANGED
@@ -22,14 +22,18 @@
|
|
22
22
|
|
23
23
|
require 'ladder_drive/plc_device'
|
24
24
|
require 'yaml'
|
25
|
+
require 'plc_plugins'
|
25
26
|
|
26
27
|
include LadderDrive
|
27
28
|
|
28
29
|
module Plc
|
29
30
|
module Emulator
|
30
31
|
|
32
|
+
|
31
33
|
class EmuPlc
|
32
34
|
|
35
|
+
include PlcPlugins
|
36
|
+
|
33
37
|
attr_accessor :program_data
|
34
38
|
attr_reader :program_pointer
|
35
39
|
attr_reader :config
|
@@ -60,6 +64,7 @@ module Emulator
|
|
60
64
|
@lock = Mutex.new
|
61
65
|
@config = config
|
62
66
|
reset
|
67
|
+
load_plugins
|
63
68
|
end
|
64
69
|
|
65
70
|
def device_by_name name
|
@@ -120,6 +125,7 @@ module Emulator
|
|
120
125
|
# Save must be executed berofe sync_output, because changed flag was clear after sync_output
|
121
126
|
save
|
122
127
|
sync_output
|
128
|
+
exec_plugins
|
123
129
|
end
|
124
130
|
|
125
131
|
def bool
|
@@ -557,8 +563,6 @@ EOB
|
|
557
563
|
end
|
558
564
|
def rst; set true; end
|
559
565
|
|
560
|
-
|
561
|
-
|
562
566
|
end
|
563
567
|
|
564
568
|
end
|
@@ -53,14 +53,15 @@ module Emulator
|
|
53
53
|
server = TCPServer.open @port
|
54
54
|
puts "launching emulator ... "
|
55
55
|
loop do
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
56
|
+
Thread.start(server.accept) do |socket|
|
57
|
+
puts "done launching"
|
58
|
+
while line = socket.gets
|
59
|
+
begin
|
60
|
+
r = @plc.execute_console_commands line
|
61
|
+
socket.puts r
|
62
|
+
rescue => e
|
63
|
+
socket.puts "E0 #{e}\r\n"
|
64
|
+
end
|
64
65
|
end
|
65
66
|
end
|
66
67
|
end
|