rmodbus 0.5.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/NEWS.md +52 -0
- data/README.md +87 -0
- data/Rakefile +22 -36
- data/examples/perfomance_rtu.rb +35 -37
- data/examples/perfomance_tcp.rb +36 -38
- data/examples/use_rtu_via_tcp_modbus.rb +8 -5
- data/examples/use_tcp_modbus.rb +10 -6
- data/lib/rmodbus/client.rb +52 -174
- data/lib/rmodbus/common.rb +45 -18
- data/lib/rmodbus/{exceptions.rb → errors.rb} +3 -0
- data/lib/rmodbus/ext.rb +25 -2
- data/lib/rmodbus/proxy.rb +54 -0
- data/lib/rmodbus/{crc16.rb → rtu.rb} +73 -2
- data/lib/rmodbus/rtu_client.rb +20 -116
- data/lib/rmodbus/rtu_server.rb +28 -57
- data/lib/rmodbus/rtu_slave.rb +59 -0
- data/lib/rmodbus/rtu_via_tcp_client.rb +22 -86
- data/lib/rmodbus/rtu_via_tcp_server.rb +31 -95
- data/lib/rmodbus/rtu_via_tcp_slave.rb +58 -0
- data/lib/rmodbus/{parsers.rb → server.rb} +24 -15
- data/lib/rmodbus/slave.rb +268 -0
- data/lib/rmodbus/sp.rb +45 -0
- data/lib/rmodbus/tcp.rb +49 -0
- data/lib/rmodbus/tcp_client.rb +19 -88
- data/lib/rmodbus/tcp_server.rb +16 -19
- data/lib/rmodbus/tcp_slave.rb +64 -0
- data/lib/rmodbus/version.rb +17 -0
- data/lib/rmodbus.rb +20 -4
- data/spec/client_spec.rb +19 -45
- data/spec/exception_spec.rb +26 -27
- data/spec/ext_spec.rb +24 -1
- data/spec/logging_spec.rb +31 -37
- data/spec/proxy_spec.rb +73 -0
- data/spec/read_rtu_response_spec.rb +2 -4
- data/spec/rtu_client_spec.rb +17 -19
- data/spec/rtu_server_spec.rb +1 -3
- data/spec/rtu_via_tcp_client_spec.rb +69 -63
- data/spec/slave_spec.rb +55 -0
- data/spec/tcp_client_spec.rb +77 -69
- data/spec/tcp_server_spec.rb +34 -49
- metadata +123 -37
- data/AUTHORS +0 -3
- data/ChangeLog +0 -82
- data/LICENSE +0 -675
- data/README +0 -53
- data/examples/add_new_function.rb +0 -19
data/lib/rmodbus/rtu_server.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# RModBus - free implementation of ModBus protocol in Ruby.
|
2
2
|
#
|
3
|
-
# Copyright (C) 2010
|
3
|
+
# Copyright (C) 2010-2011 Timin Aleksey
|
4
4
|
#
|
5
5
|
# This program is free software: you can redistribute it and/or modify
|
6
6
|
# it under the terms of the GNU General Public License as published by
|
@@ -11,78 +11,49 @@
|
|
11
11
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
12
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
13
|
# GNU General Public License for more details.
|
14
|
-
|
15
|
-
require 'rmodbus/parsers'
|
16
|
-
|
17
|
-
begin
|
18
|
-
require 'rubygems'
|
19
|
-
rescue
|
20
|
-
end
|
21
|
-
|
22
14
|
require 'serialport'
|
23
15
|
|
24
16
|
module ModBus
|
25
|
-
|
17
|
+
# RTU server implementation
|
18
|
+
# @example
|
19
|
+
# srv = RTUServer.new('/dev/ttyS1', 9600, 1)
|
20
|
+
# srv.coils = [1,0,1,1]
|
21
|
+
# srv.discrete_inputs = [1,1,0,0]
|
22
|
+
# srv.holding_registers = [1,2,3,4]
|
23
|
+
# srv.input_registers = [1,2,3,4]
|
24
|
+
# srv.debug = true
|
25
|
+
# srv.start
|
26
26
|
class RTUServer
|
27
|
-
include
|
28
|
-
include
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
warn "[DEPRECATION] `discret_inputs=` is deprecated. Please use `discrete_inputs=` instead."
|
40
|
-
@discrete_inputs=val
|
41
|
-
end
|
42
|
-
|
43
|
-
|
44
|
-
def initialize(port, baud=9600, slaveaddr=1, options = {})
|
45
|
-
Thread.abort_on_exception = true
|
46
|
-
|
47
|
-
@port, @baud = port, baud
|
48
|
-
@data_bits, @stop_bits, @parity = 8, 1, SerialPort::NONE
|
49
|
-
|
50
|
-
@data_bits = options[:data_bits] unless options[:data_bits].nil?
|
51
|
-
@stop_bits = options[:stop_bits] unless options[:stop_bits].nil?
|
52
|
-
@parity = options[:parity] unless options[:parity].nil?
|
53
|
-
|
54
|
-
@sp = SerialPort.new(@port, @baud, @data_bits, @stop_bits, @parity)
|
55
|
-
@sp.read_timeout = 5
|
56
|
-
|
57
|
-
@coils = []
|
58
|
-
@discrete_inputs = []
|
59
|
-
@holding_registers = []
|
60
|
-
@input_registers = []
|
61
|
-
@slave = slaveaddr
|
27
|
+
include Common
|
28
|
+
include Server
|
29
|
+
include RTU
|
30
|
+
include SP
|
31
|
+
|
32
|
+
# Init RTU server
|
33
|
+
# @param [Integer] uid slave device
|
34
|
+
# @see SP#open_serial_port
|
35
|
+
def initialize(port, baud=9600, uid=1, opts = {})
|
36
|
+
Thread.abort_on_exception = true
|
37
|
+
@sp = open_serial_port(port, baud, opts)
|
38
|
+
@uid = uid
|
62
39
|
end
|
63
40
|
|
41
|
+
# Start server
|
64
42
|
def start
|
65
43
|
@serv = Thread.new do
|
66
|
-
|
67
|
-
|
68
|
-
while req.size == 0
|
69
|
-
req = @sp.read
|
70
|
-
end
|
71
|
-
if req.getbyte(0) == @slave and req[-2,2].unpack('n')[0] == crc16(req[0..-3])
|
72
|
-
pdu = exec_req(req[1..-1], @coils, @discrete_inputs, @holding_registers, @input_registers)
|
73
|
-
resp = @slave.chr + pdu
|
74
|
-
resp << crc16(resp).to_word
|
75
|
-
@sp.write resp
|
76
|
-
end
|
44
|
+
serv_rtu_requests(@sp) do |msg|
|
45
|
+
exec_req(msg[1..-3], @coils, @discrete_inputs, @holding_registers, @input_registers)
|
77
46
|
end
|
78
47
|
end
|
79
48
|
end
|
80
49
|
|
50
|
+
# Stop server
|
81
51
|
def stop
|
82
52
|
Thread.kill(@serv)
|
83
|
-
@sp.close
|
53
|
+
@sp.close
|
84
54
|
end
|
85
55
|
|
56
|
+
# Join server
|
86
57
|
def join
|
87
58
|
@serv.join
|
88
59
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# RModBus - free implementation of ModBus protocol in Ruby.
|
2
|
+
#
|
3
|
+
# Copyright (C) 2011 Timin Aleksey
|
4
|
+
#
|
5
|
+
# This program is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
module ModBus
|
15
|
+
# RTU slave implementation
|
16
|
+
# @example
|
17
|
+
# RTUClient.connect(port, baud, opts) do |cl|
|
18
|
+
# cl.with_slave(uid) do |slave|
|
19
|
+
# slave.holding_registers[0..100]
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# @see RTUClient#open_connection
|
24
|
+
# @see Client#with_slave
|
25
|
+
# @see Slave
|
26
|
+
class RTUSlave < Slave
|
27
|
+
include RTU
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
# overide method for RTU implamentaion
|
32
|
+
# @see Slave#query
|
33
|
+
def send_pdu(pdu)
|
34
|
+
msg = @uid.chr + pdu
|
35
|
+
msg << crc16(msg).to_word
|
36
|
+
@io.write msg
|
37
|
+
|
38
|
+
log "Tx (#{msg.size} bytes): " + logging_bytes(msg)
|
39
|
+
end
|
40
|
+
|
41
|
+
# overide method for RTU implamentaion
|
42
|
+
# @see Slave#query
|
43
|
+
def read_pdu
|
44
|
+
msg = read_rtu_response(@io)
|
45
|
+
|
46
|
+
log "Rx (#{msg.size} bytes): " + logging_bytes(msg)
|
47
|
+
|
48
|
+
if msg.getbyte(0) == @uid
|
49
|
+
return msg[1..-3] if msg[-2,2].unpack('n')[0] == crc16(msg[0..-3])
|
50
|
+
log "Ignore package: don't match CRC"
|
51
|
+
else
|
52
|
+
log "Ignore package: don't match uid ID"
|
53
|
+
end
|
54
|
+
loop do
|
55
|
+
#waite timeout
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# RModBus - free implementation of ModBus protocol in Ruby.
|
2
2
|
#
|
3
|
-
# Copyright (C) 2009 Timin Aleksey
|
3
|
+
# Copyright (C) 2009-2011 Timin Aleksey
|
4
4
|
# Copyright (C) 2010 Kelley Reynolds
|
5
5
|
#
|
6
6
|
# This program is free software: you can redistribute it and/or modify
|
@@ -12,94 +12,30 @@
|
|
12
12
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
13
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
14
|
# GNU General Public License for more details.
|
15
|
-
require 'rmodbus/crc16'
|
16
|
-
require 'timeout'
|
17
|
-
require 'rmodbus/client'
|
18
|
-
require 'rmodbus/exceptions'
|
19
|
-
|
20
15
|
module ModBus
|
21
|
-
|
16
|
+
# RTU over TCP client implementation
|
17
|
+
# @example
|
18
|
+
# RTUViaTCPClient.connect('127.0.0.1', 10002) do |cl|
|
19
|
+
# cl.with_slave(uid) do |slave|
|
20
|
+
# slave.holding_registers[0..100]
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# @see RTUViaTCPClient#open_connection
|
25
|
+
# @see Client#initialize
|
22
26
|
class RTUViaTCPClient < Client
|
23
|
-
|
24
|
-
include
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
#
|
32
|
-
# port - port TCP connections
|
33
|
-
#
|
34
|
-
# slaveaddr - slave ID of the server
|
35
|
-
#
|
36
|
-
# RTUViaTCPClient.connect('127.0.0.1') do |cl|
|
37
|
-
#
|
38
|
-
# put cl.read_holding_registers(0, 10)
|
39
|
-
#
|
40
|
-
# end
|
41
|
-
def self.connect(ipaddr, port = 10002, slaveaddr = 1)
|
42
|
-
cl = RTUViaTCPClient.new(ipaddr, port, slaveaddr)
|
43
|
-
yield cl
|
44
|
-
cl.close
|
45
|
-
end
|
46
|
-
|
47
|
-
# Connect with a ModBus server
|
48
|
-
#
|
49
|
-
# ipaddr - ip of the server
|
50
|
-
#
|
51
|
-
# port - port TCP connections
|
52
|
-
#
|
53
|
-
# slaveaddr - slave ID of the server
|
54
|
-
def initialize(ipaddr, port = 10002, slaveaddr = 1)
|
55
|
-
@ipaddr, @port = ipaddr, port
|
56
|
-
tried = 0
|
57
|
-
begin
|
58
|
-
timeout(1, ModBusTimeout) do
|
59
|
-
@sock = TCPSocket.new(@ipaddr, @port)
|
60
|
-
end
|
61
|
-
rescue ModBusTimeout => err
|
62
|
-
raise ModBusTimeout.new, 'Timed out attempting to create connection'
|
63
|
-
end
|
64
|
-
@slave = slaveaddr
|
65
|
-
@debug = false
|
66
|
-
super()
|
67
|
-
end
|
68
|
-
|
69
|
-
# Close TCP connections
|
70
|
-
def close
|
71
|
-
@sock.close unless @sock.closed?
|
27
|
+
include RTU
|
28
|
+
include TCP
|
29
|
+
|
30
|
+
protected
|
31
|
+
# Open TCP\IP connection
|
32
|
+
# @see TCP#open_tcp_connection
|
33
|
+
def open_connection(ipaddr, port = 10002, opts = {})
|
34
|
+
io = open_tcp_connection(ipaddr, port, opts)
|
72
35
|
end
|
73
36
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
end
|
78
|
-
|
79
|
-
protected
|
80
|
-
|
81
|
-
def send_pdu(pdu)
|
82
|
-
msg = @slave.chr + pdu
|
83
|
-
msg << crc16(msg).to_word
|
84
|
-
@sock.write msg
|
85
|
-
|
86
|
-
log "Tx (#{msg.size} bytes): " + logging_bytes(msg)
|
87
|
-
end
|
88
|
-
|
89
|
-
def read_pdu
|
90
|
-
# Read the response appropriately
|
91
|
-
msg = read_rtu_response(@sock)
|
92
|
-
|
93
|
-
log "Rx (#{msg.size} bytes): " + logging_bytes(msg)
|
94
|
-
if msg.getbyte(0) == @slave
|
95
|
-
return msg[1..-3] if msg[-2,2].unpack('n')[0] == crc16(msg[0..-3])
|
96
|
-
log "Ignore package: don't match CRC"
|
97
|
-
else
|
98
|
-
log "Ignore package: don't match slave ID"
|
99
|
-
end
|
100
|
-
loop do
|
101
|
-
#waite timeout
|
102
|
-
end
|
103
|
-
end
|
37
|
+
def get_slave(uid, io)
|
38
|
+
RTUViaTCPSlave.new(uid, io)
|
39
|
+
end
|
104
40
|
end
|
105
41
|
end
|
@@ -13,102 +13,38 @@
|
|
13
13
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
14
|
# GNU General Public License for more details.
|
15
15
|
|
16
|
-
require 'rmodbus/parsers'
|
17
16
|
require 'gserver'
|
18
17
|
|
19
18
|
module ModBus
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
msg = read_modbus_rtu_request(io)
|
52
|
-
|
53
|
-
# If there is no RTU message, we're done serving this client
|
54
|
-
break if msg.nil?
|
55
|
-
|
56
|
-
if msg.getbyte(0) == @slave and msg[-2,2].unpack('n')[0] == crc16(msg[0..-3])
|
57
|
-
pdu = exec_req(msg[1..-3], @coils, @discrete_inputs, @holding_registers, @input_registers)
|
58
|
-
resp = @slave.chr + pdu
|
59
|
-
resp << crc16(resp).to_word
|
60
|
-
log "Server TX (#{resp.size} bytes): #{logging_bytes(resp)}"
|
61
|
-
io.write resp
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
private
|
67
|
-
|
68
|
-
# We have to read specific amounts of numbers of bytes from the network depending on the function code and content
|
69
|
-
# NOTE: The initial read could be increased to 7 and that would let us cobine the two reads for functions 15 and 16 but this method is more clear
|
70
|
-
def read_modbus_rtu_request(io)
|
71
|
-
# Read the slave_id and function code
|
72
|
-
msg = io.read(2)
|
73
|
-
|
74
|
-
# If msg is nil, then our client never sent us anything and it's time to disconnect
|
75
|
-
return if msg.nil?
|
76
|
-
|
77
|
-
function_code = msg.getbyte(1)
|
78
|
-
if [1, 2, 3, 4, 5, 6].include?(function_code)
|
79
|
-
# read 6 more bytes and return the message total message
|
80
|
-
msg += io.read(6)
|
81
|
-
elsif [15, 16].include?(function_code)
|
82
|
-
# Read in first register, register count, and data bytes
|
83
|
-
msg += io.read(5)
|
84
|
-
# Read in however much data we need to + 2 CRC bytes
|
85
|
-
msg += io.read(msg.getbyte(6) + 2)
|
86
|
-
else
|
87
|
-
raise ModBus::Errors::IllegalFunction, "Illegal function: #{function_code}"
|
88
|
-
end
|
89
|
-
|
90
|
-
log "Server RX (#{msg.size} bytes): #{logging_bytes(msg)}"
|
91
|
-
|
92
|
-
msg
|
93
|
-
end
|
94
|
-
|
95
|
-
def log(msg)
|
96
|
-
if @debug
|
97
|
-
$stdout.puts msg
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
def logging_bytes(msg)
|
102
|
-
result = ""
|
103
|
-
msg.each_byte do |c|
|
104
|
-
byte = if c < 16
|
105
|
-
'0' + c.to_s(16)
|
106
|
-
else
|
107
|
-
c.to_s(16)
|
108
|
-
end
|
109
|
-
result << "[#{byte}]"
|
110
|
-
end
|
111
|
-
result
|
112
|
-
end
|
113
|
-
end
|
19
|
+
# RTU over TCP server implementation
|
20
|
+
# @example
|
21
|
+
# srv = RTUViaTCPServer.new(10002, 1)
|
22
|
+
# srv.coils = [1,0,1,1]
|
23
|
+
# srv.discrete_inputs = [1,1,0,0]
|
24
|
+
# srv.holding_registers = [1,2,3,4]
|
25
|
+
# srv.input_registers = [1,2,3,4]
|
26
|
+
# srv.debug = true
|
27
|
+
# srv.start
|
28
|
+
class RTUViaTCPServer < GServer
|
29
|
+
include Common
|
30
|
+
include RTU
|
31
|
+
include Server
|
32
|
+
|
33
|
+
# Init server
|
34
|
+
# @param [Integer] port listen port
|
35
|
+
# @param [Integer] uid slave device
|
36
|
+
def initialize(port = 10002, uid = 1)
|
37
|
+
@uid = uid
|
38
|
+
super(port)
|
39
|
+
end
|
40
|
+
|
41
|
+
protected
|
42
|
+
# Serve requests
|
43
|
+
# @param [TCPSocket] io socket
|
44
|
+
def serve(io)
|
45
|
+
serv_rtu_requests(io) do |msg|
|
46
|
+
exec_req(msg[1..-3], @coils, @discrete_inputs, @holding_registers, @input_registers)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
114
50
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# RModBus - free implementation of ModBus protocol in Ruby.
|
2
|
+
#
|
3
|
+
# Copyright (C) 2011 Timin Aleksey
|
4
|
+
#
|
5
|
+
# This program is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
module ModBus
|
15
|
+
# RTU over TCP slave implementation
|
16
|
+
# @example
|
17
|
+
# RTUViaTCP.connect('127.0.0.1', 10002) do |cl|
|
18
|
+
# cl.with_slave(uid) do |slave|
|
19
|
+
# slave.holding_registers[0..100]
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# @see RTUViaTCPClient#open_connection
|
24
|
+
# @see Client#with_slave
|
25
|
+
# @see Slave
|
26
|
+
class RTUViaTCPSlave < Slave
|
27
|
+
include RTU
|
28
|
+
|
29
|
+
protected
|
30
|
+
# overide method for RTU over TCP implamentaion
|
31
|
+
# @see Slave#query
|
32
|
+
def send_pdu(pdu)
|
33
|
+
msg = @uid.chr + pdu
|
34
|
+
msg << crc16(msg).to_word
|
35
|
+
@io.write msg
|
36
|
+
|
37
|
+
log "Tx (#{msg.size} bytes): " + logging_bytes(msg)
|
38
|
+
end
|
39
|
+
|
40
|
+
# overide method for RTU over TCP implamentaion
|
41
|
+
# @see Slave#query
|
42
|
+
def read_pdu
|
43
|
+
# Read the response appropriately
|
44
|
+
msg = read_rtu_response(@io)
|
45
|
+
|
46
|
+
log "Rx (#{msg.size} bytes): " + logging_bytes(msg)
|
47
|
+
if msg.getbyte(0) == @uid
|
48
|
+
return msg[1..-3] if msg[-2,2].unpack('n')[0] == crc16(msg[0..-3])
|
49
|
+
log "Ignore package: don't match CRC"
|
50
|
+
else
|
51
|
+
log "Ignore package: don't match uid ID"
|
52
|
+
end
|
53
|
+
loop do
|
54
|
+
#waite timeout
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -1,6 +1,7 @@
|
|
1
|
-
# RModBus - free implementation of ModBus protocol in
|
1
|
+
# RModBus - free implementation of ModBus protocol in Ruby.
|
2
2
|
#
|
3
3
|
# Copyright (C) 2010 Timin Aleksey
|
4
|
+
# Copyright (C) 2010 Kelley Reynolds
|
4
5
|
#
|
5
6
|
# This program is free software: you can redistribute it and/or modify
|
6
7
|
# it under the terms of the GNU General Public License as published by
|
@@ -13,17 +14,25 @@
|
|
13
14
|
# GNU General Public License for more details.
|
14
15
|
|
15
16
|
module ModBus
|
16
|
-
|
17
|
-
|
17
|
+
# Module for implementation ModBus server
|
18
|
+
module Server
|
18
19
|
Funcs = [1,2,3,4,5,6,15,16]
|
19
20
|
|
21
|
+
attr_accessor :coils, :discrete_inputs, :holding_registers, :input_registers, :uid
|
22
|
+
@coils = []
|
23
|
+
@discrete_inputs = []
|
24
|
+
@holding_registers =[]
|
25
|
+
@input_registers = []
|
26
|
+
|
27
|
+
private
|
28
|
+
|
20
29
|
def exec_req(req, coils, discrete_inputs, holding_registers, input_registers)
|
21
30
|
func = req.getbyte(0)
|
22
|
-
|
31
|
+
|
23
32
|
unless Funcs.include?(func)
|
24
33
|
params = { :err => 1 }
|
25
34
|
end
|
26
|
-
|
35
|
+
|
27
36
|
case func
|
28
37
|
when 1
|
29
38
|
params = parse_read_func(req, coils)
|
@@ -47,7 +56,7 @@ module ModBus
|
|
47
56
|
if params[:err] == 0
|
48
57
|
pdu = func.chr + (params[:quant] * 2).chr + input_registers[params[:addr],params[:quant]].pack('n*')
|
49
58
|
end
|
50
|
-
when 5
|
59
|
+
when 5
|
51
60
|
params = parse_write_coil_func(req)
|
52
61
|
if params[:err] == 0
|
53
62
|
coils[params[:addr]] = params[:val]
|
@@ -72,24 +81,23 @@ module ModBus
|
|
72
81
|
pdu = req[0,5]
|
73
82
|
end
|
74
83
|
end
|
75
|
-
|
84
|
+
|
76
85
|
if params[:err] == 0
|
77
|
-
pdu
|
86
|
+
pdu
|
78
87
|
else
|
79
88
|
pdu = (func | 0x80).chr + params[:err].chr
|
80
89
|
end
|
81
90
|
end
|
82
91
|
|
83
|
-
private
|
84
92
|
def parse_read_func(req, field)
|
85
93
|
quant = req[3,2].unpack('n')[0]
|
86
94
|
|
87
95
|
return { :err => 3} unless quant <= 0x7d
|
88
|
-
|
96
|
+
|
89
97
|
addr = req[1,2].unpack('n')[0]
|
90
98
|
return { :err => 2 } unless addr + quant <= field.size
|
91
|
-
|
92
|
-
return { :err => 0, :quant => quant, :addr => addr }
|
99
|
+
|
100
|
+
return { :err => 0, :quant => quant, :addr => addr }
|
93
101
|
end
|
94
102
|
|
95
103
|
def parse_write_coil_func(req)
|
@@ -98,9 +106,9 @@ module ModBus
|
|
98
106
|
|
99
107
|
val = req[3,2].unpack('n')[0]
|
100
108
|
return { :err => 3 } unless val == 0 or val == 0xff00
|
101
|
-
|
109
|
+
|
102
110
|
val = 1 if val == 0xff00
|
103
|
-
return { :err => 0, :addr => addr, :val => val }
|
111
|
+
return { :err => 0, :addr => addr, :val => val }
|
104
112
|
end
|
105
113
|
|
106
114
|
def parse_write_register_func(req)
|
@@ -109,7 +117,7 @@ module ModBus
|
|
109
117
|
|
110
118
|
val = req[3,2].unpack('n')[0]
|
111
119
|
|
112
|
-
return { :err => 0, :addr => addr, :val => val }
|
120
|
+
return { :err => 0, :addr => addr, :val => val }
|
113
121
|
end
|
114
122
|
|
115
123
|
def parse_write_multiple_coils_func(req)
|
@@ -129,5 +137,6 @@ module ModBus
|
|
129
137
|
end
|
130
138
|
params
|
131
139
|
end
|
140
|
+
|
132
141
|
end
|
133
142
|
end
|