rmodbus 1.0.4 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 [![Build Status](https://secure.travis-ci.org/flipback/rmodbus.png)](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