rmodbus 1.0.4 → 1.1.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 +22 -5
- data/README.md +3 -23
- data/Rakefile +3 -1
- data/lib/rmodbus/client.rb +12 -0
- data/lib/rmodbus/{common.rb → debug.rb} +4 -6
- data/lib/rmodbus/errors.rb +9 -3
- data/lib/rmodbus/ext.rb +7 -1
- data/lib/rmodbus/options.rb +21 -0
- data/lib/rmodbus/rtu.rb +28 -0
- data/lib/rmodbus/rtu_server.rb +2 -3
- data/lib/rmodbus/rtu_slave.rb +3 -20
- data/lib/rmodbus/rtu_via_tcp_server.rb +1 -1
- data/lib/rmodbus/rtu_via_tcp_slave.rb +9 -25
- data/lib/rmodbus/slave.rb +67 -12
- data/lib/rmodbus/sp.rb +2 -3
- data/lib/rmodbus/tcp_server.rb +52 -54
- data/lib/rmodbus/version.rb +2 -2
- data/lib/rmodbus.rb +6 -3
- data/spec/client_spec.rb +54 -7
- data/spec/exception_spec.rb +13 -15
- data/spec/logging_spec.rb +7 -6
- data/spec/read_rtu_response_spec.rb +3 -4
- data/spec/response_mismach_spec.rb +162 -0
- data/spec/rtu_client_spec.rb +4 -5
- data/spec/rtu_server_spec.rb +3 -4
- data/spec/rtu_via_tcp_client_spec.rb +5 -8
- data/spec/slave_spec.rb +2 -3
- data/spec/spec_helper.rb +54 -0
- data/spec/tcp_client_spec.rb +4 -6
- data/spec/tcp_server_spec.rb +11 -11
- metadata +45 -14
data/NEWS.md
CHANGED
@@ -1,6 +1,23 @@
|
|
1
|
+
2011-10-29 Release 1.1.0
|
2
|
+
===================================
|
3
|
+
1. Fixed issue [#12](https://github.com/flipback/rmodbus/issues/12). Added option Slave#raise_exception_on_mismatch to turn to check response and raise exception
|
4
|
+
if it's mismatch.
|
5
|
+
2. Added pass options :debug, :raise_exception_on_mismatch, :read_retry_timeout, :read_retries from clients to slaves
|
6
|
+
`ruby
|
7
|
+
@cl.debug = true
|
8
|
+
@cl.with_slave(1) do |slave_1|
|
9
|
+
slave_1.debug #=> true
|
10
|
+
end
|
11
|
+
@cl.with_slave(2) do |slave_2|
|
12
|
+
slave_2.debug = false
|
13
|
+
slave_2.debug #=> false
|
14
|
+
end
|
15
|
+
`
|
16
|
+
3. Deleted dependency with `serialport` gem. Install it manual for using RTU
|
17
|
+
|
1
18
|
2011-08-10 Release 1.0.4
|
2
19
|
====================================
|
3
|
-
1. Fixed issue [#11](https://github.com/flipback/rmodbus/issues/
|
20
|
+
1. Fixed issue [#11](https://github.com/flipback/rmodbus/issues/11)
|
4
21
|
|
5
22
|
|
6
23
|
2011-07-17 Release 1.0.3
|
@@ -27,7 +44,7 @@ New API for client part of library
|
|
27
44
|
---------------------------------------
|
28
45
|
|
29
46
|
Example:
|
30
|
-
|
47
|
+
`ruby
|
31
48
|
require 'rmodbus'
|
32
49
|
|
33
50
|
ModBus::TCPClient.new('127.0.0.1', 8502) do |cl|
|
@@ -45,7 +62,7 @@ Example:
|
|
45
62
|
slave.holding_registers[16..20] = [1, 2, 3, 4, 5]
|
46
63
|
end
|
47
64
|
end
|
48
|
-
|
65
|
+
`
|
49
66
|
for more information [see](http://rdoc.info/gems/rmodbus/1.0.0/frames)
|
50
67
|
|
51
68
|
Conversion to/from 32bit registers
|
@@ -54,7 +71,7 @@ Conversion to/from 32bit registers
|
|
54
71
|
Some modbus devices use two registers to store 32bit values.
|
55
72
|
RModbus provides some helper functions to go back and forth between these two things when reading/writing.
|
56
73
|
The built-in examples assume registers in a particular order but it's trivial to change.
|
57
|
-
|
74
|
+
`ruby
|
58
75
|
# Reading values in multiple registers (you can read more than 2 and convert them all so long as they are in multiples of 2)
|
59
76
|
res = slave.holding_registers[0..1]
|
60
77
|
res.inspect => [20342, 17344]
|
@@ -66,7 +83,7 @@ The built-in examples assume registers in a particular order but it's trivial to
|
|
66
83
|
cl.holding_registers[0..1] => [20342, 17344]
|
67
84
|
cl.holding_registers[2..3] = [384.620788574219].from_32f
|
68
85
|
cl.holding_registers[2..3] => [20342, 17344]
|
69
|
-
|
86
|
+
`
|
70
87
|
Support JRuby
|
71
88
|
--------------------------------------
|
72
89
|
Now you could use RModBus on JRuby without RTU implementation.
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
RModBus
|
1
|
+
RModBus [](http://travis-ci.org/flipback/rmodbus)
|
2
2
|
==========================
|
3
3
|
|
4
4
|
**RModBus** - free implementation of protocol ModBus.
|
@@ -50,26 +50,6 @@ Example
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
-
|
54
|
-
Conversion to/from 32bit registers
|
55
|
-
-----------------------------------
|
56
|
-
|
57
|
-
Some modbus devices use two registers to store 32bit values.
|
58
|
-
RModbus provides some helper functions to go back and forth between these two things when reading/writing.
|
59
|
-
The built-in examples assume registers in a particular order but it's trivial to change.
|
60
|
-
|
61
|
-
# Reading values in multiple registers (you can read more than 2 and convert them all so long as they are in multiples of 2)
|
62
|
-
res = slave.holding_registers[0..1]
|
63
|
-
res.inspect => [20342, 17344]
|
64
|
-
res.to_32i => [1136676726]
|
65
|
-
res.to_32f => [384.620788574219]
|
66
|
-
|
67
|
-
# Writing 32b values to multiple registers
|
68
|
-
cl.holding_registers[0..1] = [1136676726].from_32i
|
69
|
-
cl.holding_registers[0..1] => [20342, 17344]
|
70
|
-
cl.holding_registers[2..3] = [384.620788574219].from_32f
|
71
|
-
cl.holding_registers[2..3] => [20342, 17344]
|
72
|
-
|
73
53
|
GitHub
|
74
54
|
----------------------------------
|
75
55
|
|
@@ -80,8 +60,8 @@ You can checkout source code from GitHub repositry
|
|
80
60
|
Reference
|
81
61
|
----------------------------------
|
82
62
|
|
83
|
-
Home page: http://rmodbus.
|
63
|
+
Home page: http://rmodbus.flipback.net
|
84
64
|
|
85
65
|
RModBud on GitHub: http://github.com/flipback/RModBus
|
86
66
|
|
87
|
-
ModBus community: http://www.modbus-ida.org
|
67
|
+
ModBus community: http://www.modbus-ida.org
|
data/Rakefile
CHANGED
@@ -15,7 +15,9 @@ require 'rspec/core'
|
|
15
15
|
require 'rspec/core/rake_task'
|
16
16
|
RSpec::Core::RakeTask.new(:spec) do |spec|
|
17
17
|
spec.pattern = FileList['spec/**/*_spec.rb']
|
18
|
-
|
18
|
+
begin
|
19
|
+
require 'serialport'
|
20
|
+
rescue LoadError => e
|
19
21
|
spec.pattern.exclude("spec/rtu_client_spec.rb", "spec/rtu_server_spec.rb")
|
20
22
|
end
|
21
23
|
end
|
data/lib/rmodbus/client.rb
CHANGED
@@ -16,6 +16,8 @@ module ModBus
|
|
16
16
|
# @abstract
|
17
17
|
class Client
|
18
18
|
include Errors
|
19
|
+
include Debug
|
20
|
+
include Options
|
19
21
|
|
20
22
|
# Initialized client (alias :connect)
|
21
23
|
# @example
|
@@ -26,6 +28,12 @@ module ModBus
|
|
26
28
|
# @yield return client object and close it before exit
|
27
29
|
# @return [Client] client object
|
28
30
|
def initialize(*args, &block)
|
31
|
+
# Defaults
|
32
|
+
@debug = false
|
33
|
+
@raise_exception_on_mismatch = false
|
34
|
+
@read_retry_timeout = 1
|
35
|
+
@read_retries = 10
|
36
|
+
|
29
37
|
@io = open_connection(*args)
|
30
38
|
if block_given?
|
31
39
|
yield self
|
@@ -50,6 +58,10 @@ module ModBus
|
|
50
58
|
# @return [Slave] slave object
|
51
59
|
def with_slave(uid, &block)
|
52
60
|
slave = get_slave(uid, @io)
|
61
|
+
slave.debug = debug
|
62
|
+
slave.raise_exception_on_mismatch = raise_exception_on_mismatch
|
63
|
+
slave.read_retries = read_retries
|
64
|
+
slave.read_retry_timeout = read_retry_timeout
|
53
65
|
if block_given?
|
54
66
|
yield slave
|
55
67
|
else
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# RModBus - free implementation of ModBus protocol in Ruby.
|
2
2
|
#
|
3
|
-
# Copyright (C) 2010 Timin Aleksey
|
3
|
+
# Copyright (C) 2010-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
|
@@ -14,12 +14,10 @@
|
|
14
14
|
# GNU General Public License for more details.
|
15
15
|
|
16
16
|
module ModBus
|
17
|
-
module
|
18
|
-
|
19
|
-
|
20
|
-
attr_accessor :debug
|
17
|
+
module Debug
|
18
|
+
attr_accessor :debug, :raise_exception_on_mismatch,
|
19
|
+
:read_retries, :read_retry_timeout
|
21
20
|
|
22
|
-
@debug = false
|
23
21
|
|
24
22
|
private
|
25
23
|
# Put log message on standart output
|
data/lib/rmodbus/errors.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# RModBus - free implementation of ModBus protocol on Ruby.
|
2
2
|
#
|
3
|
-
# Copyright (C) 2008 Timin Aleksey
|
3
|
+
# Copyright (C) 2008-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
|
@@ -14,7 +14,6 @@
|
|
14
14
|
module ModBus
|
15
15
|
|
16
16
|
module Errors
|
17
|
-
|
18
17
|
class ProxyException < StandardError
|
19
18
|
end
|
20
19
|
|
@@ -45,6 +44,13 @@ module ModBus
|
|
45
44
|
class ModBusTimeout < ModBusException
|
46
45
|
end
|
47
46
|
|
47
|
+
class ResponseMismatch < ModBusException
|
48
|
+
attr_reader :request, :response
|
49
|
+
def initialize(msg, request, response)
|
50
|
+
super(msg)
|
51
|
+
@request = request
|
52
|
+
@response = response
|
53
|
+
end
|
54
|
+
end
|
48
55
|
end
|
49
|
-
|
50
56
|
end
|
data/lib/rmodbus/ext.rb
CHANGED
@@ -0,0 +1,21 @@
|
|
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
|
+
|
15
|
+
module ModBus
|
16
|
+
module Options
|
17
|
+
attr_accessor :raise_exception_on_mismatch,
|
18
|
+
:read_retries, :read_retry_timeout
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
data/lib/rmodbus/rtu.rb
CHANGED
@@ -23,6 +23,7 @@ module ModBus
|
|
23
23
|
msg = nil
|
24
24
|
while msg.nil?
|
25
25
|
msg = io.read(2)
|
26
|
+
sleep(0.01)
|
26
27
|
end
|
27
28
|
|
28
29
|
function_code = msg.getbyte(1)
|
@@ -44,6 +45,32 @@ module ModBus
|
|
44
45
|
end
|
45
46
|
end
|
46
47
|
|
48
|
+
def send_rtu_pdu(pdu)
|
49
|
+
msg = @uid.chr + pdu
|
50
|
+
msg << crc16(msg).to_word
|
51
|
+
@io.write msg
|
52
|
+
|
53
|
+
log "Tx (#{msg.size} bytes): " + logging_bytes(msg)
|
54
|
+
end
|
55
|
+
|
56
|
+
def read_rtu_pdu
|
57
|
+
msg = read_rtu_response(@io)
|
58
|
+
|
59
|
+
log "Rx (#{msg.size} bytes): " + logging_bytes(msg)
|
60
|
+
|
61
|
+
if msg.getbyte(0) == @uid
|
62
|
+
return msg[1..-3] if msg[-2,2].unpack('n')[0] == crc16(msg[0..-3])
|
63
|
+
log "Ignore package: don't match CRC"
|
64
|
+
else
|
65
|
+
log "Ignore package: don't match uid ID"
|
66
|
+
end
|
67
|
+
loop do
|
68
|
+
#waite timeout
|
69
|
+
sleep(0.1)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
|
47
74
|
def read_rtu_request(io)
|
48
75
|
# Read the slave_id and function code
|
49
76
|
msg = io.read(2)
|
@@ -83,6 +110,7 @@ module ModBus
|
|
83
110
|
log "Server TX (#{resp.size} bytes): #{logging_bytes(resp)}"
|
84
111
|
io.write resp
|
85
112
|
end
|
113
|
+
sleep(0.01)
|
86
114
|
end
|
87
115
|
end
|
88
116
|
|
data/lib/rmodbus/rtu_server.rb
CHANGED
@@ -11,8 +11,7 @@
|
|
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
|
-
|
14
|
+
#
|
16
15
|
module ModBus
|
17
16
|
# RTU server implementation
|
18
17
|
# @example
|
@@ -24,7 +23,7 @@ module ModBus
|
|
24
23
|
# srv.debug = true
|
25
24
|
# srv.start
|
26
25
|
class RTUServer
|
27
|
-
include
|
26
|
+
include Debug
|
28
27
|
include Server
|
29
28
|
include RTU
|
30
29
|
include SP
|
data/lib/rmodbus/rtu_slave.rb
CHANGED
@@ -26,34 +26,17 @@ module ModBus
|
|
26
26
|
class RTUSlave < Slave
|
27
27
|
include RTU
|
28
28
|
|
29
|
-
|
30
|
-
|
29
|
+
private
|
31
30
|
# overide method for RTU implamentaion
|
32
31
|
# @see Slave#query
|
33
32
|
def send_pdu(pdu)
|
34
|
-
|
35
|
-
msg << crc16(msg).to_word
|
36
|
-
@io.write msg
|
37
|
-
|
38
|
-
log "Tx (#{msg.size} bytes): " + logging_bytes(msg)
|
33
|
+
send_rtu_pdu(pdu)
|
39
34
|
end
|
40
35
|
|
41
36
|
# overide method for RTU implamentaion
|
42
37
|
# @see Slave#query
|
43
38
|
def read_pdu
|
44
|
-
|
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
|
39
|
+
read_rtu_pdu
|
57
40
|
end
|
58
41
|
end
|
59
42
|
end
|
@@ -26,33 +26,17 @@ module ModBus
|
|
26
26
|
class RTUViaTCPSlave < Slave
|
27
27
|
include RTU
|
28
28
|
|
29
|
-
|
30
|
-
# overide method for RTU
|
29
|
+
private
|
30
|
+
# overide method for RTU implamentaion
|
31
31
|
# @see Slave#query
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
@io.write msg
|
32
|
+
def send_pdu(pdu)
|
33
|
+
send_rtu_pdu(pdu)
|
34
|
+
end
|
36
35
|
|
37
|
-
|
38
|
-
end
|
39
|
-
|
40
|
-
# overide method for RTU over TCP implamentaion
|
36
|
+
# overide method for RTU implamentaion
|
41
37
|
# @see Slave#query
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
38
|
+
def read_pdu
|
39
|
+
read_rtu_pdu
|
40
|
+
end
|
57
41
|
end
|
58
42
|
end
|
data/lib/rmodbus/slave.rb
CHANGED
@@ -16,10 +16,10 @@
|
|
16
16
|
module ModBus
|
17
17
|
class Slave
|
18
18
|
include Errors
|
19
|
-
|
19
|
+
include Debug
|
20
|
+
include Options
|
20
21
|
# Number of times to retry on read and read timeouts
|
21
|
-
attr_accessor :
|
22
|
-
|
22
|
+
attr_accessor :uid
|
23
23
|
Exceptions = {
|
24
24
|
1 => IllegalFunction.new("The function code received in the query is not an allowable action for the server"),
|
25
25
|
2 => IllegalDataAddress.new("The data address received in the query is not an allowable address for the server"),
|
@@ -31,8 +31,6 @@ module ModBus
|
|
31
31
|
}
|
32
32
|
def initialize(uid, io)
|
33
33
|
@uid = uid
|
34
|
-
@read_retries = 10
|
35
|
-
@read_retry_timeout = 1
|
36
34
|
@io = io
|
37
35
|
end
|
38
36
|
|
@@ -231,6 +229,7 @@ module ModBus
|
|
231
229
|
# @param [String] pdu request to slave
|
232
230
|
# @return [String] received data
|
233
231
|
#
|
232
|
+
# @raise [ResponseMismatch] the received echo response differs from the request
|
234
233
|
# @raise [ModBusTimeout] timed out during read attempt
|
235
234
|
# @raise [ModBusException] unknown error
|
236
235
|
# @raise [IllegalFunction] function code received in the query is not an allowable action for the server
|
@@ -240,12 +239,13 @@ module ModBus
|
|
240
239
|
# @raise [Acknowledge] server has accepted the request and is processing it, but a long duration of time will be required to do so
|
241
240
|
# @raise [SlaveDeviceBus] server is engaged in processing a long duration program command
|
242
241
|
# @raise [MemoryParityError] extended file area failed to pass a consistency check
|
243
|
-
def query(
|
242
|
+
def query(request)
|
244
243
|
tried = 0
|
244
|
+
response = ""
|
245
245
|
begin
|
246
246
|
timeout(@read_retry_timeout, ModBusTimeout) do
|
247
|
-
send_pdu(
|
248
|
-
|
247
|
+
send_pdu(request)
|
248
|
+
response = read_pdu
|
249
249
|
end
|
250
250
|
rescue ModBusTimeout => err
|
251
251
|
log "Timeout of read operation: (#{@read_retries - tried})"
|
@@ -254,15 +254,70 @@ module ModBus
|
|
254
254
|
raise ModBusTimeout.new, "Timed out during read attempt"
|
255
255
|
end
|
256
256
|
|
257
|
-
return nil if
|
257
|
+
return nil if response.size == 0
|
258
258
|
|
259
|
-
|
260
|
-
|
259
|
+
read_func = response.getbyte(0)
|
260
|
+
if read_func >= 0x80
|
261
|
+
exc_id = response.getbyte(1)
|
261
262
|
raise Exceptions[exc_id] unless Exceptions[exc_id].nil?
|
262
263
|
|
263
264
|
raise ModBusException.new, "Unknown error"
|
264
265
|
end
|
265
|
-
|
266
|
+
|
267
|
+
check_response_mismatch(request, response) if raise_exception_on_mismatch
|
268
|
+
response[2..-1]
|
269
|
+
end
|
270
|
+
|
271
|
+
private
|
272
|
+
def check_response_mismatch(request, response)
|
273
|
+
read_func = response.getbyte(0)
|
274
|
+
data = response[2..-1]
|
275
|
+
#Mismatch functional code
|
276
|
+
send_func = request.getbyte(0)
|
277
|
+
if read_func != send_func
|
278
|
+
msg = "Function code is mismatch (expected #{send_func}, got #{read_func})"
|
279
|
+
end
|
280
|
+
|
281
|
+
case read_func
|
282
|
+
when 1,2
|
283
|
+
bc = request.getword(3)/8 + 1
|
284
|
+
if data.size != bc
|
285
|
+
msg = "Byte count is mismatch (expected #{bc}, got #{data.size} bytes)"
|
286
|
+
end
|
287
|
+
when 3,4
|
288
|
+
rc = request.getword(3)
|
289
|
+
if data.size/2 != rc
|
290
|
+
msg = "Register count is mismatch (expected #{rc}, got #{data.size/2} regs)"
|
291
|
+
end
|
292
|
+
when 5,6
|
293
|
+
exp_addr = request.getword(1)
|
294
|
+
got_addr = response.getword(1)
|
295
|
+
if exp_addr != got_addr
|
296
|
+
msg = "Address is mismatch (expected #{exp_addr}, got #{got_addr})"
|
297
|
+
end
|
298
|
+
|
299
|
+
exp_val = request.getword(3)
|
300
|
+
got_val = response.getword(3)
|
301
|
+
if exp_val != got_val
|
302
|
+
msg = "Value is mismatch (expected 0x#{exp_val.to_s(16)}, got 0x#{got_val.to_s(16)})"
|
303
|
+
end
|
304
|
+
when 15,16
|
305
|
+
exp_addr = request.getword(1)
|
306
|
+
got_addr = response.getword(1)
|
307
|
+
if exp_addr != got_addr
|
308
|
+
msg = "Address is mismatch (expected #{exp_addr}, got #{got_addr})"
|
309
|
+
end
|
310
|
+
|
311
|
+
exp_quant = request.getword(3)
|
312
|
+
got_quant = response.getword(3)
|
313
|
+
if exp_quant != got_quant
|
314
|
+
msg = "Quantity is mismatch (expected #{exp_quant}, got #{got_quant})"
|
315
|
+
end
|
316
|
+
else
|
317
|
+
warn "Fuiction (#{read_func}) is not supported raising response mismatch"
|
318
|
+
end
|
319
|
+
|
320
|
+
raise ResponseMismatch.new(msg, request, response) if msg
|
266
321
|
end
|
267
322
|
end
|
268
323
|
end
|
data/lib/rmodbus/sp.rb
CHANGED
@@ -12,8 +12,6 @@
|
|
12
12
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
13
|
# GNU General Public License for more details.
|
14
14
|
|
15
|
-
require 'serialport'
|
16
|
-
|
17
15
|
module ModBus
|
18
16
|
module SP
|
19
17
|
attr_reader :port, :baud, :data_bits, :stop_bits, :parity, :read_timeout
|
@@ -42,4 +40,5 @@ module ModBus
|
|
42
40
|
io
|
43
41
|
end
|
44
42
|
end
|
45
|
-
end
|
43
|
+
end
|
44
|
+
|
data/lib/rmodbus/tcp_server.rb
CHANGED
@@ -1,19 +1,19 @@
|
|
1
|
-
# RModBus - free implementation of ModBus protocol on Ruby.
|
2
|
-
#
|
3
|
-
# Copyright (C) 2008 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
|
-
require 'gserver'
|
15
|
-
|
16
|
-
module ModBus
|
1
|
+
# RModBus - free implementation of ModBus protocol on Ruby.
|
2
|
+
#
|
3
|
+
# Copyright (C) 2008-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
|
+
require 'gserver'
|
15
|
+
|
16
|
+
module ModBus
|
17
17
|
# TCP server implementation
|
18
18
|
# @example
|
19
19
|
# srv = TCPServer.new(10002, 1)
|
@@ -22,45 +22,43 @@ module ModBus
|
|
22
22
|
# srv.holding_registers = [1,2,3,4]
|
23
23
|
# srv.input_registers = [1,2,3,4]
|
24
24
|
# srv.debug = true
|
25
|
-
# srv.start
|
26
|
-
class TCPServer < GServer
|
27
|
-
include
|
28
|
-
include Server
|
29
|
-
|
25
|
+
# srv.start
|
26
|
+
class TCPServer < GServer
|
27
|
+
include Debug
|
28
|
+
include Server
|
29
|
+
|
30
30
|
# Init server
|
31
31
|
# @param [Integer] port listen port
|
32
|
-
# @param [Integer] uid slave device
|
33
|
-
# @param [Hash] opts options of server
|
32
|
+
# @param [Integer] uid slave device
|
33
|
+
# @param [Hash] opts options of server
|
34
34
|
# @option opts [String] :host host of server default '127.0.0.1'
|
35
|
-
# @option opts [Float, Integer] :max_connection max of TCP connection with server default 4
|
36
|
-
def initialize(port = 502, uid = 1, opts = {})
|
37
|
-
@uid = uid
|
38
|
-
opts[:host] = DEFAULT_HOST unless opts[:host]
|
39
|
-
opts[:max_connection] = 4 unless opts[:max_connection]
|
40
|
-
super(port, host = opts[:host], maxConnection = opts[:max_connection])
|
41
|
-
end
|
42
|
-
|
35
|
+
# @option opts [Float, Integer] :max_connection max of TCP connection with server default 4
|
36
|
+
def initialize(port = 502, uid = 1, opts = {})
|
37
|
+
@uid = uid
|
38
|
+
opts[:host] = DEFAULT_HOST unless opts[:host]
|
39
|
+
opts[:max_connection] = 4 unless opts[:max_connection]
|
40
|
+
super(port, host = opts[:host], maxConnection = opts[:max_connection])
|
41
|
+
end
|
42
|
+
|
43
43
|
# Serve requests
|
44
|
-
# @param [TCPSocket] io socket
|
45
|
-
def serve(io)
|
46
|
-
|
47
|
-
req = io.read(7)
|
48
|
-
if req[2,2]
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
end
|
66
|
-
end
|
44
|
+
# @param [TCPSocket] io socket
|
45
|
+
def serve(io)
|
46
|
+
while not stopped?
|
47
|
+
req = io.read(7)
|
48
|
+
if req[2,2] == "\x00\x00" or req.getbyte(6) == @uid
|
49
|
+
tr = req[0,2]
|
50
|
+
len = req[4,2].unpack('n')[0]
|
51
|
+
req = io.read(len - 1)
|
52
|
+
log "Server RX (#{req.size} bytes): #{logging_bytes(req)}"
|
53
|
+
|
54
|
+
pdu = exec_req(req, @coils, @discrete_inputs, @holding_registers, @input_registers)
|
55
|
+
|
56
|
+
resp = tr + "\0\0" + (pdu.size + 1).to_word + @uid.chr + pdu
|
57
|
+
log "Server TX (#{resp.size} bytes): #{logging_bytes(resp)}"
|
58
|
+
io.write resp
|
59
|
+
end
|
60
|
+
sleep(0.01)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/rmodbus/version.rb
CHANGED