rmodbus 0.5.0 → 1.0.0
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.
- 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
|