rmodbus-ccutrer 2.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.
- 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
|