rmodbus-ccutrer 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/NEWS.md +180 -0
- data/README.md +115 -0
- data/Rakefile +29 -0
- data/examples/perfomance_rtu.rb +56 -0
- data/examples/perfomance_rtu_via_tcp.rb +55 -0
- data/examples/perfomance_tcp.rb +55 -0
- data/examples/simple-xpca-gateway.rb +84 -0
- data/examples/use_rtu_via_tcp_modbus.rb +22 -0
- data/examples/use_tcp_modbus.rb +23 -0
- data/lib/rmodbus.rb +21 -0
- data/lib/rmodbus/client.rb +94 -0
- data/lib/rmodbus/client/slave.rb +345 -0
- data/lib/rmodbus/debug.rb +25 -0
- data/lib/rmodbus/errors.rb +42 -0
- data/lib/rmodbus/ext.rb +85 -0
- data/lib/rmodbus/options.rb +6 -0
- data/lib/rmodbus/proxy.rb +41 -0
- data/lib/rmodbus/rtu.rb +122 -0
- data/lib/rmodbus/rtu_client.rb +43 -0
- data/lib/rmodbus/rtu_server.rb +48 -0
- data/lib/rmodbus/rtu_slave.rb +48 -0
- data/lib/rmodbus/rtu_via_tcp_server.rb +35 -0
- data/lib/rmodbus/server.rb +246 -0
- data/lib/rmodbus/server/slave.rb +16 -0
- data/lib/rmodbus/sp.rb +36 -0
- data/lib/rmodbus/tcp.rb +31 -0
- data/lib/rmodbus/tcp_client.rb +25 -0
- data/lib/rmodbus/tcp_server.rb +67 -0
- data/lib/rmodbus/tcp_slave.rb +55 -0
- data/lib/rmodbus/version.rb +3 -0
- data/spec/client_spec.rb +88 -0
- data/spec/exception_spec.rb +120 -0
- data/spec/ext_spec.rb +52 -0
- data/spec/logging_spec.rb +89 -0
- data/spec/proxy_spec.rb +74 -0
- data/spec/read_rtu_response_spec.rb +92 -0
- data/spec/response_mismach_spec.rb +163 -0
- data/spec/rtu_client_spec.rb +86 -0
- data/spec/rtu_server_spec.rb +31 -0
- data/spec/rtu_via_tcp_client_spec.rb +76 -0
- data/spec/rtu_via_tcp_server_spec.rb +89 -0
- data/spec/slave_spec.rb +55 -0
- data/spec/spec_helper.rb +54 -0
- data/spec/tcp_client_spec.rb +88 -0
- data/spec/tcp_server_spec.rb +158 -0
- metadata +206 -0
@@ -0,0 +1,89 @@
|
|
1
|
+
# -*- coding: ascii
|
2
|
+
require "rmodbus"
|
3
|
+
|
4
|
+
describe ModBus::RTUViaTCPServer do
|
5
|
+
before :all do
|
6
|
+
@port = 8502
|
7
|
+
begin
|
8
|
+
@server = ModBus::RTUViaTCPServer.new(@port)
|
9
|
+
@server_slave = @server.with_slave(1)
|
10
|
+
@server_slave.coils = [1,0,1,1]
|
11
|
+
@server_slave.discrete_inputs = [1,1,0,0]
|
12
|
+
@server_slave.holding_registers = [1,2,3,4]
|
13
|
+
@server_slave.input_registers = [1,2,3,4]
|
14
|
+
@server.promiscuous = true
|
15
|
+
@server.start
|
16
|
+
rescue Errno::EADDRINUSE
|
17
|
+
@port += 1
|
18
|
+
retry
|
19
|
+
end
|
20
|
+
@cl = ModBus::RTUClient.new('127.0.0.1', @port)
|
21
|
+
@cl.read_retries = 1
|
22
|
+
@slave = @cl.with_slave(1)
|
23
|
+
# pretend this is a serialport and we're just someone else on the same bus
|
24
|
+
@io = @cl.instance_variable_get(:@io)
|
25
|
+
end
|
26
|
+
|
27
|
+
before do
|
28
|
+
@server.debug = false
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should have options :host" do
|
32
|
+
host = '192.168.0.1'
|
33
|
+
srv = ModBus::RTUViaTCPServer.new(1010, :host => '192.168.0.1')
|
34
|
+
srv.host.should eql(host)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should have options :max_connection" do
|
38
|
+
max_conn = 5
|
39
|
+
srv = ModBus::RTUViaTCPServer.new(1010, :max_connection => 5)
|
40
|
+
srv.maxConnections.should eql(max_conn)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should properly ignore responses from other slaves" do
|
44
|
+
request = "\x10\x03\x0\x1\x0\x1\xd6\x8b"
|
45
|
+
response = "\x10\x83\x1\xd0\xf5"
|
46
|
+
@server.debug = true
|
47
|
+
@server.should receive(:log).ordered.with("Server RX (8 bytes): [10][03][00][01][00][01][d6][8b]")
|
48
|
+
@server.should receive(:log).ordered.with("Server RX function 3 to 16: {:quant=>1, :addr=>1}")
|
49
|
+
@server.should receive(:log).ordered.with("Server RX (5 bytes): [10][83][01][d0][f5]")
|
50
|
+
@server.should receive(:log).ordered.with("Server RX response 3 from 16: {:err=>1}")
|
51
|
+
@server.should receive(:log).ordered.with("Server RX (8 bytes): [01][01][00][00][00][01][fd][ca]")
|
52
|
+
@server.should receive(:log).ordered.with("Server RX function 1 to 1: {:quant=>1, :addr=>0}")
|
53
|
+
@server.should receive(:log).ordered.with("Server TX (6 bytes): [01][01][01][01][90][48]")
|
54
|
+
@io.write(request)
|
55
|
+
@io.write(response)
|
56
|
+
# just to prove the server can still handle subsequent requests
|
57
|
+
@slave.read_coils(0, 1).should == [1]
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should properly ignore functions from other slaves that it doesn't understand" do
|
61
|
+
request = "\x10\x41\x0\x1\x0\x1\x0\x5\x0\x1\xb1\x00"
|
62
|
+
response = "\x10\xc1\x1\xe0\x55"
|
63
|
+
@io.write(request)
|
64
|
+
@io.write(response)
|
65
|
+
# just to prove the server can still handle subsequent requests
|
66
|
+
@slave.read_coils(0, 1).should == [1]
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should properly ignore utter garbage on the line from starting up halfway through a conversation" do
|
70
|
+
response = "garbage" * 50 + "\x1\x55\xe0"
|
71
|
+
@io.write(response)
|
72
|
+
# just to prove the server can still handle subsequent requests
|
73
|
+
@slave.read_coils(0, 1).should == [1]
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should send exception if request is malformed" do
|
77
|
+
lambda { @slave.query("\x01\x01") }.should raise_exception(
|
78
|
+
ModBus::Errors::ModBusTimeout)
|
79
|
+
end
|
80
|
+
|
81
|
+
after :all do
|
82
|
+
@cl.close unless @cl.closed?
|
83
|
+
@server.stop unless @server.stopped?
|
84
|
+
while GServer.in_service?(@port)
|
85
|
+
sleep(0.01)
|
86
|
+
end
|
87
|
+
@server.stop
|
88
|
+
end
|
89
|
+
end
|
data/spec/slave_spec.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# -*- coding: ascii
|
2
|
+
require 'rmodbus'
|
3
|
+
|
4
|
+
describe ModBus::Client::Slave do
|
5
|
+
before do
|
6
|
+
@slave = ModBus::Client.new.with_slave(1)
|
7
|
+
|
8
|
+
@slave.stub(:query).and_return('')
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should support function 'read coils'" do
|
12
|
+
@slave.should_receive(:query).with("\x1\x0\x13\x0\x13").and_return("\xcd\x6b\x5")
|
13
|
+
@slave.read_coils(0x13,0x13).should == [1,0,1,1, 0,0,1,1, 1,1,0,1, 0,1,1,0, 1,0,1]
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should support function 'read discrete inputs'" do
|
17
|
+
@slave.should_receive(:query).with("\x2\x0\xc4\x0\x16").and_return("\xac\xdb\x35")
|
18
|
+
@slave.read_discrete_inputs(0xc4,0x16).should == [0,0,1,1, 0,1,0,1, 1,1,0,1, 1,0,1,1, 1,0,1,0, 1,1]
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should support function 'read holding registers'" do
|
22
|
+
@slave.should_receive(:query).with("\x3\x0\x6b\x0\x3").and_return("\x2\x2b\x0\x0\x0\x64")
|
23
|
+
@slave.read_holding_registers(0x6b,0x3).should == [0x022b, 0x0000, 0x0064]
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should support function 'read input registers'" do
|
27
|
+
@slave.should_receive(:query).with("\x4\x0\x8\x0\x1").and_return("\x0\xa")
|
28
|
+
@slave.read_input_registers(0x8,0x1).should == [0x000a]
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should support function 'write single coil'" do
|
32
|
+
@slave.should_receive(:query).with("\x5\x0\xac\xff\x0").and_return("\xac\xff\x00")
|
33
|
+
@slave.write_single_coil(0xac,0x1).should == @slave
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should support function 'write single register'" do
|
37
|
+
@slave.should_receive(:query).with("\x6\x0\x1\x0\x3").and_return("\x1\x0\x3")
|
38
|
+
@slave.write_single_register(0x1,0x3).should == @slave
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should support function 'write multiple coils'" do
|
42
|
+
@slave.should_receive(:query).with("\xf\x0\x13\x0\xa\x2\xcd\x1").and_return("\x13\x0\xa")
|
43
|
+
@slave.write_multiple_coils(0x13,[1,0,1,1, 0,0,1,1, 1,0]).should == @slave
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should support function 'write multiple registers'" do
|
47
|
+
@slave.should_receive(:query).with("\x10\x0\x1\x0\x3\x6\x0\xa\x1\x2\xf\xf").and_return("\x1\x0\x3")
|
48
|
+
@slave.write_multiple_registers(0x1,[0x000a,0x0102, 0xf0f]).should == @slave
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should support function 'mask write register'" do
|
52
|
+
@slave.should_receive(:query).with("\x16\x0\x4\x0\xf2\x0\2").and_return("\x4\x0\xf2\x0\x2")
|
53
|
+
@slave.mask_write_register(0x4, 0xf2, 0x2).should == @slave
|
54
|
+
end
|
55
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# -*- coding: ascii
|
2
|
+
require "rmodbus"
|
3
|
+
|
4
|
+
class RaiseResponseMismatch
|
5
|
+
def initialize(message, request, response)
|
6
|
+
@expected_message, @expected_request, @expected_response = message, request, response
|
7
|
+
end
|
8
|
+
|
9
|
+
def matches?(given_block)
|
10
|
+
begin
|
11
|
+
given_block.call
|
12
|
+
rescue ModBus::Errors::ResponseMismatch => e
|
13
|
+
@actual_message = e.message
|
14
|
+
@actual_request = e.request
|
15
|
+
@actual_response = e.response
|
16
|
+
|
17
|
+
@with_expected_message = verify_message
|
18
|
+
@with_expected_request = @expected_request == @actual_request
|
19
|
+
@with_expected_response = @expected_response == @actual_response
|
20
|
+
end
|
21
|
+
@with_expected_message & @with_expected_request & @with_expected_response
|
22
|
+
end
|
23
|
+
|
24
|
+
def failure_message
|
25
|
+
unless @with_expected_message
|
26
|
+
return "Expected message '#{@expected_message}', got '#{@actual_message}'"
|
27
|
+
end
|
28
|
+
|
29
|
+
unless @with_expected_request
|
30
|
+
return "Expected request #{logging_bytes @expected_request}, got #{logging_bytes @actual_request}"
|
31
|
+
end
|
32
|
+
|
33
|
+
unless @with_expected_response
|
34
|
+
return "Expected response #{logging_bytes @expected_response}, got #{logging_bytes @actual_response}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def verify_message
|
39
|
+
case @expected_message
|
40
|
+
when nil
|
41
|
+
true
|
42
|
+
when Regexp
|
43
|
+
@expected_message =~ @actual_message
|
44
|
+
else
|
45
|
+
@expected_message == @actual_message
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
module RaiseResponseMatcher
|
51
|
+
def raise_response_mismatch(message, request, response)
|
52
|
+
RaiseResponseMismatch.new(message, request, response)
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# -*- coding: ascii
|
2
|
+
require 'rmodbus'
|
3
|
+
|
4
|
+
describe ModBus::TCPClient do
|
5
|
+
describe "method 'query'" do
|
6
|
+
before(:each) do
|
7
|
+
@uid = 1
|
8
|
+
@sock = double('Socket')
|
9
|
+
@adu = "\000\001\000\000\000\001\001"
|
10
|
+
|
11
|
+
Socket.should_receive(:tcp).with('127.0.0.1', 1502, nil, nil, hash_including(:connect_timeout)).and_return(@sock)
|
12
|
+
@sock.stub(:read).with(0).and_return('')
|
13
|
+
@cl = ModBus::TCPClient.new('127.0.0.1', 1502)
|
14
|
+
@slave = @cl.with_slave(@uid)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should send valid MBAP Header' do
|
18
|
+
@adu[0,2] = @slave.transaction.next.to_word
|
19
|
+
@sock.should_receive(:write).with(@adu)
|
20
|
+
@sock.should_receive(:read).with(7).and_return(@adu)
|
21
|
+
@slave.query('').should == nil
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should not throw exception and white next packet if get other transaction' do
|
25
|
+
@adu[0,2] = @slave.transaction.next.to_word
|
26
|
+
@sock.should_receive(:write).with(@adu)
|
27
|
+
@sock.should_receive(:read).with(7).and_return("\000\002\000\000\000\001" + @uid.chr)
|
28
|
+
@sock.should_receive(:read).with(7).and_return("\000\001\000\000\000\001" + @uid.chr)
|
29
|
+
|
30
|
+
expect{ @slave.query('') }.to_not raise_error
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should throw timeout exception if do not get own transaction' do
|
34
|
+
@slave.read_retries = 2
|
35
|
+
@adu[0,2] = @slave.transaction.next.to_word
|
36
|
+
@sock.should_receive(:write).at_least(1).times.with(/\.*/)
|
37
|
+
@sock.should_receive(:read).at_least(1).times.with(7).and_return("\000\x3\000\000\000\001" + @uid.chr)
|
38
|
+
|
39
|
+
expect{ @slave.query('') }.to raise_error(ModBus::Errors::ModBusTimeout, "Timed out during read attempt")
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
it 'should return only data from PDU' do
|
44
|
+
request = "\x3\x0\x6b\x0\x3"
|
45
|
+
response = "\x3\x6\x2\x2b\x0\x0\x0\x64"
|
46
|
+
@adu = @slave.transaction.next.to_word + "\x0\x0\x0\x9" + @uid.chr + request
|
47
|
+
@sock.should_receive(:write).with(@adu[0,4] + "\0\6" + @uid.chr + request)
|
48
|
+
@sock.should_receive(:read).with(7).and_return(@adu[0,7])
|
49
|
+
@sock.should_receive(:read).with(8).and_return(response)
|
50
|
+
|
51
|
+
@slave.query(request).should == response[2..-1]
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should sugar connect method' do
|
55
|
+
ipaddr, port = '127.0.0.1', 502
|
56
|
+
Socket.should_receive(:tcp).with(ipaddr, port, nil, nil, hash_including(:connect_timeout)).and_return(@sock)
|
57
|
+
@sock.should_receive(:closed?).and_return(false)
|
58
|
+
@sock.should_receive(:close)
|
59
|
+
ModBus::TCPClient.connect(ipaddr, port) do |cl|
|
60
|
+
cl.ipaddr.should == ipaddr
|
61
|
+
cl.port.should == port
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should have closed? method' do
|
66
|
+
@sock.should_receive(:closed?).and_return(false)
|
67
|
+
@cl.closed?.should == false
|
68
|
+
|
69
|
+
@sock.should_receive(:closed?).and_return(false)
|
70
|
+
@sock.should_receive(:close)
|
71
|
+
|
72
|
+
@cl.close
|
73
|
+
|
74
|
+
@sock.should_receive(:closed?).and_return(true)
|
75
|
+
@cl.closed?.should == true
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should give slave object in block' do
|
79
|
+
@cl.with_slave(1) do |slave|
|
80
|
+
slave.uid = 1
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should tune connection timeout" do
|
86
|
+
lambda { ModBus::TCPClient.new('81.123.231.11', 1999, :connect_timeout => 0.001) }.should raise_error(ModBus::Errors::ModBusTimeout)
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
# -*- coding: ascii
|
2
|
+
require "rmodbus"
|
3
|
+
|
4
|
+
describe ModBus::TCPServer do
|
5
|
+
before :all do
|
6
|
+
unit_ids = (1..247).to_a.shuffle
|
7
|
+
valid_unit_id = unit_ids.first
|
8
|
+
@invalid_unit_id = unit_ids.last
|
9
|
+
@port = 8502
|
10
|
+
begin
|
11
|
+
@server = ModBus::TCPServer.new(@port)
|
12
|
+
@server_slave = @server.with_slave(valid_unit_id)
|
13
|
+
@server_slave.coils = [1,0,1,1]
|
14
|
+
@server_slave.discrete_inputs = [1,1,0,0]
|
15
|
+
@server_slave.holding_registers = [1,2,3,4]
|
16
|
+
@server_slave.input_registers = [1,2,3,4]
|
17
|
+
@server.start
|
18
|
+
rescue Errno::EADDRINUSE
|
19
|
+
@port += 1
|
20
|
+
retry
|
21
|
+
end
|
22
|
+
@cl = ModBus::TCPClient.new('127.0.0.1', @port)
|
23
|
+
@cl.read_retries = 1
|
24
|
+
@slave = @cl.with_slave(valid_unit_id)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should succeed if UID is broadcast" do
|
28
|
+
@cl.with_slave(0).write_coil(1,1)
|
29
|
+
# have to wait for the server to process it
|
30
|
+
sleep 1
|
31
|
+
@server_slave.coils[1].should == 1
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should fail if UID is mismatched" do
|
35
|
+
lambda { @cl.with_slave(@invalid_unit_id).read_coils(1,3) }.should raise_exception(
|
36
|
+
ModBus::Errors::ModBusTimeout
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should send exception if function not supported" do
|
41
|
+
lambda { @slave.query('0x43') }.should raise_exception(
|
42
|
+
ModBus::Errors::IllegalFunction,
|
43
|
+
"The function code received in the query is not an allowable action for the server"
|
44
|
+
)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should send exception if quanity of registers are more than 0x7d" do
|
48
|
+
lambda { @slave.read_holding_registers(0, 0x7e) }.should raise_exception(
|
49
|
+
ModBus::Errors::IllegalDataValue,
|
50
|
+
"A value contained in the query data field is not an allowable value for server"
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "shouldn't send exception if quanity of coils are more than 0x7d0" do
|
55
|
+
lambda { @slave.read_coils(0, 0x7d1) }.should raise_exception(
|
56
|
+
ModBus::Errors::IllegalDataValue,
|
57
|
+
"A value contained in the query data field is not an allowable value for server"
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should send exception if addr not valid" do
|
62
|
+
lambda { @slave.read_coils(2, 8) }.should raise_exception(
|
63
|
+
ModBus::Errors::IllegalDataAddress,
|
64
|
+
"The data address received in the query is not an allowable address for the server"
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should send exception if function not supported" do
|
69
|
+
lambda { @slave.query('0x43') }.should raise_exception(
|
70
|
+
ModBus::Errors::IllegalFunction,
|
71
|
+
"The function code received in the query is not an allowable action for the server"
|
72
|
+
)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should calc a many requests" do
|
76
|
+
@slave.read_coils(1,2)
|
77
|
+
@slave.write_multiple_registers(0,[9,9,9,])
|
78
|
+
@slave.read_holding_registers(0,3).should == [9,9,9]
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should supported function 'read coils'" do
|
82
|
+
@slave.read_coils(0,3).should == @server_slave.coils[0,3]
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should supported function 'read coils' with more than 125 in one request" do
|
86
|
+
@server_slave.coils = Array.new( 1900, 1 )
|
87
|
+
@slave.read_coils(0,1900).should == @server_slave.coils[0,1900]
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should supported function 'read discrete inputs'" do
|
91
|
+
@slave.read_discrete_inputs(1,3).should == @server_slave.discrete_inputs[1,3]
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should supported function 'read holding registers'" do
|
95
|
+
@slave.read_holding_registers(0,3).should == @server_slave.holding_registers[0,3]
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should supported function 'read input registers'" do
|
99
|
+
@slave.read_input_registers(2,2).should == @server_slave.input_registers[2,2]
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should supported function 'write single coil'" do
|
103
|
+
@server_slave.coils[3] = 0
|
104
|
+
@slave.write_single_coil(3,1)
|
105
|
+
@server_slave.coils[3].should == 1
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should supported function 'write single register'" do
|
109
|
+
@server_slave.holding_registers[3] = 25
|
110
|
+
@slave.write_single_register(3,35)
|
111
|
+
@server_slave.holding_registers[3].should == 35
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should supported function 'write multiple coils'" do
|
115
|
+
@server_slave.coils = [1,1,1,0, 0,0,0,0, 0,0,0,0, 0,1,1,1]
|
116
|
+
@slave.write_multiple_coils(3, [1, 0,1,0,1, 0,1,0,1])
|
117
|
+
@server_slave.coils.should == [1,1,1,1, 0,1,0,1, 0,1,0,1, 0,1,1,1]
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should supported function 'write multiple registers'" do
|
121
|
+
@server_slave.holding_registers = [1,2,3,4,5,6,7,8,9]
|
122
|
+
@slave.write_multiple_registers(3,[1,2,3,4,5])
|
123
|
+
@server_slave.holding_registers.should == [1,2,3,1,2,3,4,5,9]
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should support function 'mask_write_register'" do
|
127
|
+
@server_slave.holding_registers = [0x12]
|
128
|
+
@slave.mask_write_register(0, 0xf2, 0x25)
|
129
|
+
@server_slave.holding_registers.should == [0x17]
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should support function 'read_write_multiple_registers'" do
|
133
|
+
@server_slave.holding_registers = [1,2,3,4,5,6,7,8,9]
|
134
|
+
@slave.read_write_multiple_registers(0, 5, 4, [3,2,1]).should == [1,2,3,4,3]
|
135
|
+
@server_slave.holding_registers.should == [1,2,3,4,3,2,1,8,9]
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should have options :host" do
|
139
|
+
host = '192.168.0.1'
|
140
|
+
srv = ModBus::TCPServer.new(1010, :host => '192.168.0.1')
|
141
|
+
srv.host.should eql(host)
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should have options :max_connection" do
|
145
|
+
max_conn = 5
|
146
|
+
srv = ModBus::TCPServer.new(1010, :max_connection => 5)
|
147
|
+
srv.maxConnections.should eql(max_conn)
|
148
|
+
end
|
149
|
+
|
150
|
+
after :all do
|
151
|
+
@cl.close unless @cl.closed?
|
152
|
+
@server.stop unless @server.stopped?
|
153
|
+
while GServer.in_service?(@port)
|
154
|
+
sleep(0.01)
|
155
|
+
end
|
156
|
+
@server.stop
|
157
|
+
end
|
158
|
+
end
|