rmodbus-ccutrer 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/NEWS.md +180 -0
  3. data/README.md +115 -0
  4. data/Rakefile +29 -0
  5. data/examples/perfomance_rtu.rb +56 -0
  6. data/examples/perfomance_rtu_via_tcp.rb +55 -0
  7. data/examples/perfomance_tcp.rb +55 -0
  8. data/examples/simple-xpca-gateway.rb +84 -0
  9. data/examples/use_rtu_via_tcp_modbus.rb +22 -0
  10. data/examples/use_tcp_modbus.rb +23 -0
  11. data/lib/rmodbus.rb +21 -0
  12. data/lib/rmodbus/client.rb +94 -0
  13. data/lib/rmodbus/client/slave.rb +345 -0
  14. data/lib/rmodbus/debug.rb +25 -0
  15. data/lib/rmodbus/errors.rb +42 -0
  16. data/lib/rmodbus/ext.rb +85 -0
  17. data/lib/rmodbus/options.rb +6 -0
  18. data/lib/rmodbus/proxy.rb +41 -0
  19. data/lib/rmodbus/rtu.rb +122 -0
  20. data/lib/rmodbus/rtu_client.rb +43 -0
  21. data/lib/rmodbus/rtu_server.rb +48 -0
  22. data/lib/rmodbus/rtu_slave.rb +48 -0
  23. data/lib/rmodbus/rtu_via_tcp_server.rb +35 -0
  24. data/lib/rmodbus/server.rb +246 -0
  25. data/lib/rmodbus/server/slave.rb +16 -0
  26. data/lib/rmodbus/sp.rb +36 -0
  27. data/lib/rmodbus/tcp.rb +31 -0
  28. data/lib/rmodbus/tcp_client.rb +25 -0
  29. data/lib/rmodbus/tcp_server.rb +67 -0
  30. data/lib/rmodbus/tcp_slave.rb +55 -0
  31. data/lib/rmodbus/version.rb +3 -0
  32. data/spec/client_spec.rb +88 -0
  33. data/spec/exception_spec.rb +120 -0
  34. data/spec/ext_spec.rb +52 -0
  35. data/spec/logging_spec.rb +89 -0
  36. data/spec/proxy_spec.rb +74 -0
  37. data/spec/read_rtu_response_spec.rb +92 -0
  38. data/spec/response_mismach_spec.rb +163 -0
  39. data/spec/rtu_client_spec.rb +86 -0
  40. data/spec/rtu_server_spec.rb +31 -0
  41. data/spec/rtu_via_tcp_client_spec.rb +76 -0
  42. data/spec/rtu_via_tcp_server_spec.rb +89 -0
  43. data/spec/slave_spec.rb +55 -0
  44. data/spec/spec_helper.rb +54 -0
  45. data/spec/tcp_client_spec.rb +88 -0
  46. data/spec/tcp_server_spec.rb +158 -0
  47. metadata +206 -0
@@ -0,0 +1,92 @@
1
+ # -*- coding: ascii
2
+ require 'rmodbus'
3
+
4
+ #Use public wrap method
5
+ class ModBus::Client
6
+ include ModBus::RTU
7
+ def test_read_method(msg)
8
+ io = TestIO.new(msg)
9
+ read_rtu_response(io)
10
+ end
11
+
12
+ end
13
+
14
+ class TestIO
15
+ def initialize(msg)
16
+ @msg = msg
17
+ end
18
+
19
+ def read(num)
20
+ result = @msg[0,num]
21
+ @msg = @msg[num..-1]
22
+ result
23
+ end
24
+ end
25
+
26
+ describe "#read_rtu_response" do
27
+ before do
28
+ @cl_mb = ModBus::Client.new
29
+ end
30
+
31
+ it "should read response for 'read coils'" do
32
+ resp = make_resp("\x1\x3\xcd\x6b\x05")
33
+ @cl_mb.test_read_method(resp).should == resp
34
+ end
35
+
36
+ it "should read response for 'read discrete inputs'" do
37
+ resp = make_resp("\x2\x3\xac\xdb\x35")
38
+ @cl_mb.test_read_method(resp).should == resp
39
+ end
40
+
41
+ it "should read response for 'read holding registers'" do
42
+ resp = make_resp("\x3\x6\x2\x2b\x0\x0\x0\x64")
43
+ @cl_mb.test_read_method(resp).should == resp
44
+ end
45
+
46
+ it "should read response for 'read input registers'" do
47
+ resp = make_resp("\x4\x2\x0\xa")
48
+ @cl_mb.test_read_method(resp).should == resp
49
+ end
50
+
51
+ it "should read response for 'write single coil'" do
52
+ resp = make_resp("\x5\x0\xac\xff\x0")
53
+ @cl_mb.test_read_method(resp).should == resp
54
+ end
55
+
56
+ it "should read response for 'write single register'" do
57
+ resp = make_resp("\x6\x0\x1\x0\x3")
58
+ @cl_mb.test_read_method(resp).should == resp
59
+ end
60
+
61
+ it "should read response for 'write multiple coils'" do
62
+ resp = make_resp("\xf\x0\x13\x0\xa")
63
+ @cl_mb.test_read_method(resp).should == resp
64
+ end
65
+
66
+ it "should read response for 'write multiple registers'" do
67
+ resp = make_resp("\x10\x0\x1\x0\x2")
68
+ @cl_mb.test_read_method(resp).should == resp
69
+ end
70
+
71
+ it "should read response 'mask write register'" do
72
+ resp = make_resp("\x16\x0\x4\x0\xf2\x0\x25")
73
+ @cl_mb.test_read_method(resp).should == resp
74
+ end
75
+
76
+ it "should read exception codes" do
77
+ resp = make_resp("\x84\x3")
78
+ @cl_mb.test_read_method(resp).should == resp
79
+ end
80
+
81
+ it "should raise exception if function is illegal" do
82
+ resp = make_resp("\x1f\x0\x1\x0\x2")
83
+ lambda{ @cl_mb.test_read_method(resp)}.should raise_error {
84
+ ModBus::Errors::IllegalFunction
85
+ }
86
+ end
87
+
88
+ def make_resp(msg)
89
+ "\x1" + msg + "\x2\x2" # slave + msg + mock_crc
90
+ end
91
+ end
92
+
@@ -0,0 +1,163 @@
1
+ # -*- coding: ascii
2
+ require "spec_helper"
3
+
4
+ describe "response mismach" do
5
+ include RaiseResponseMatcher
6
+ before(:each) do
7
+ @slave = ModBus::Client::Slave.new(1, nil)
8
+ @slave.raise_exception_on_mismatch = true
9
+ end
10
+
11
+ it "should raise error if function code is mismatch" do
12
+ request = "\x1\x0\x13\x0\x12"
13
+ response = "\x2\x3\xcd\xb6\x5"
14
+ mock_query!(request, response)
15
+
16
+ lambda{ @slave.read_coils(0x13,0x12) }.should raise_response_mismatch(
17
+ "Function code is mismatch (expected 1, got 2)",
18
+ request, response)
19
+ end
20
+
21
+ describe "read coils" do
22
+ it "should raise error if count of byte is mismatch" do
23
+ request = "\x1\x0\x13\x0\x12"
24
+ response = "\x1\x2\xcd\xb6"
25
+ mock_query!(request, response)
26
+
27
+ lambda{ @slave.read_coils(0x13,0x12) }.should raise_response_mismatch(
28
+ "Byte count is mismatch (expected 3, got 2 bytes)",
29
+ request, response)
30
+ end
31
+ end
32
+
33
+ describe "read discrete inputs" do
34
+ it "should raise error if count of byte is mismatch" do
35
+ request = "\x2\x0\x13\x0\x12"
36
+ response = "\x2\x2\xcd\xb6"
37
+ mock_query!(request, response)
38
+
39
+ lambda{ @slave.read_discrete_inputs(0x13,0x12) }.should raise_response_mismatch(
40
+ "Byte count is mismatch (expected 3, got 2 bytes)",
41
+ request, response)
42
+ end
43
+ end
44
+
45
+ describe "read holding registesrs" do
46
+ it "should raise error if count of registers is mismatch" do
47
+ request = "\x3\x0\x8\x0\x1"
48
+ response = "\x3\x4\x0\xa\x0\xb"
49
+ mock_query!(request, response)
50
+
51
+ lambda{ @slave.read_holding_registers(0x8,0x1) }.should raise_response_mismatch(
52
+ "Register count is mismatch (expected 1, got 2 regs)",
53
+ request, response)
54
+ end
55
+ end
56
+
57
+ describe "read input registesrs" do
58
+ it "should raise error if count of registers is mismatch" do
59
+ request = "\x4\x0\x8\x0\x2"
60
+ response = "\x4\x2\xa\x0"
61
+ mock_query!(request, response)
62
+
63
+ lambda{ @slave.read_input_registers(0x8,0x2) }.should raise_response_mismatch(
64
+ "Register count is mismatch (expected 2, got 1 regs)",
65
+ request, response)
66
+ end
67
+ end
68
+
69
+ describe "write single coil" do
70
+ it "should raise error if address of coil is mismatch" do
71
+ request = "\x5\x0\x8\xff\x0"
72
+ response = "\x5\x0\x9\xff\x0"
73
+ mock_query!(request, response)
74
+
75
+ lambda{ @slave.write_coil(8,true) }.should raise_response_mismatch(
76
+ "Address is mismatch (expected 8, got 9)",
77
+ request, response)
78
+ end
79
+
80
+ it "should raise error if value of coil is mismatch" do
81
+ request = "\x5\x0\x8\xff\x0"
82
+ response = "\x5\x0\x8\x0\x0"
83
+ mock_query!(request, response)
84
+
85
+ lambda{ @slave.write_coil(8,true) }.should raise_response_mismatch(
86
+ "Value is mismatch (expected 0xff00, got 0x0)",
87
+ request, response)
88
+ end
89
+ end
90
+
91
+ describe "write single register" do
92
+ it "should raise error if address of register is mismatch" do
93
+ request = "\x6\x0\x8\xa\xb"
94
+ response = "\x6\x0\x9\xa\xb"
95
+ mock_query!(request, response)
96
+
97
+ lambda{ @slave.write_single_register(8,0x0a0b) }.should raise_response_mismatch(
98
+ "Address is mismatch (expected 8, got 9)",
99
+ request, response)
100
+ end
101
+
102
+ it "should raise error if value of register is mismatch" do
103
+ request = "\x6\x0\x8\xa\xb"
104
+ response = "\x6\x0\x8\x9\xb"
105
+ mock_query!(request, response)
106
+
107
+ lambda{ @slave.write_single_register(8,0x0a0b) }.should raise_response_mismatch(
108
+ "Value is mismatch (expected 0xa0b, got 0x90b)",
109
+ request, response)
110
+ end
111
+ end
112
+
113
+ describe "write multiple coils" do
114
+ it "should raise error if address of first coil is mismatch" do
115
+ request = "\xf\x0\x13\x0\xa\2\xcd\x01"
116
+ response = "\xf\x0\x14\x0\xa"
117
+ mock_query!(request, response)
118
+
119
+ lambda{ @slave.write_coils(0x13,[1,0,1,1, 0,0,1,1, 1,0]) }.should raise_response_mismatch(
120
+ "Address is mismatch (expected 19, got 20)",
121
+ request, response)
122
+ end
123
+
124
+ it "should raise error if quantity of coils is mismatch" do
125
+ request = "\xf\x0\x13\x0\xa\2\xcd\x01"
126
+ response = "\xf\x0\x13\x0\x9"
127
+ mock_query!(request, response)
128
+
129
+ lambda{ @slave.write_coils(0x13,[1,0,1,1, 0,0,1,1, 1,0]) }.should raise_response_mismatch(
130
+ "Quantity is mismatch (expected 10, got 9)",
131
+ request, response)
132
+ end
133
+ end
134
+
135
+ describe "write multiple registers" do
136
+ it "should raise error if address of first register is mismatch" do
137
+ request = "\x10\x0\x1\x0\x2\x4\x0\xa\x1\x2"
138
+ response = "\x10\x0\x2\x0\x2"
139
+ mock_query!(request, response)
140
+
141
+ lambda{ @slave.write_holding_registers(0x1,[0xa,0x102]) }.should raise_response_mismatch(
142
+ "Address is mismatch (expected 1, got 2)",
143
+ request, response)
144
+ end
145
+
146
+ it "should raise error if quantity of registers is mismatch" do
147
+ request = "\x10\x0\x1\x0\x2\x4\x0\xa\x1\x2"
148
+ response = "\x10\x0\x2\x0\x1"
149
+ mock_query!(request, response)
150
+
151
+ lambda{ @slave.write_holding_registers(0x1,[0xa,0x102]) }.should raise_response_mismatch(
152
+ "Quantity is mismatch (expected 2, got 1)",
153
+ request, response)
154
+ end
155
+ end
156
+
157
+
158
+ private
159
+ def mock_query!(request, response)
160
+ @slave.should_receive(:send_pdu).with(request)
161
+ @slave.should_receive(:read_pdu).and_return(response)
162
+ end
163
+ end
@@ -0,0 +1,86 @@
1
+ # -*- coding: ascii
2
+ require 'rmodbus'
3
+
4
+ describe ModBus::RTUClient do
5
+ before do
6
+ @sp = double('Serial port')
7
+
8
+ SerialPort.should_receive(:new).with("/dev/port1", 9600, 8, 1, 0).and_return(@sp)
9
+ SerialPort.stub(:public_method_defined?).with(:flush_input).and_return(true)
10
+
11
+ @sp.stub(:read_timeout=)
12
+ @sp.stub(:class).and_return(SerialPort)
13
+ @sp.should_receive(:flow_control=).with(SerialPort::NONE)
14
+ @sp.stub(:flush_input)
15
+
16
+ @cl = ModBus::RTUClient.new("/dev/port1", 9600, :data_bits => 8, :stop_bits => 1, :parity => SerialPort::NONE)
17
+ @slave = @cl.with_slave(1)
18
+ @slave.read_retries = 1
19
+ end
20
+
21
+ it "should ignore frame with other UID" do
22
+ request = "\x10\x0\x1\x0\x1\x2\xff\xff"
23
+ @sp.should_receive(:write).with("\1#{request}\xA6\x31")
24
+ @sp.should_receive(:read).with(2).and_return("\x2\x10")
25
+ @sp.should_receive(:read).with(6).and_return("\x0\x1\x0\x1\x1C\x08")
26
+ lambda {@slave.query(request)}.should raise_error(ModBus::Errors::ModBusTimeout)
27
+ end
28
+
29
+ it "should ignored frame with incorrect CRC" do
30
+ request = "\x10\x0\x1\x0\x1\x2\xff\xff"
31
+ @sp.should_receive(:write).with("\1#{request}\xA6\x31")
32
+ @sp.should_receive(:read).with(2).and_return("\x2\x10")
33
+ @sp.should_receive(:read).with(6).and_return("\x0\x1\x0\x1\x1C\x08")
34
+ lambda {@slave.query(request)}.should raise_error(ModBus::Errors::ModBusTimeout)
35
+ end
36
+
37
+ it "should return value of registers"do
38
+ request = stab_valid_request
39
+ @slave.query(request).should == "\xff\xff"
40
+ end
41
+
42
+ it 'should sugar connect method' do
43
+ port, baud = "/dev/port1", 4800
44
+ SerialPort.should_receive(:new).with(port, baud, 8, 1, SerialPort::NONE).and_return(@sp)
45
+ @sp.should_receive(:closed?).and_return(false)
46
+ @sp.should_receive(:close)
47
+ @sp.should_receive(:flow_control=).with(SerialPort::NONE)
48
+ ModBus::RTUClient.connect(port, baud) do |cl|
49
+ cl.port.should == port
50
+ cl.baud.should == baud
51
+ cl.data_bits.should == 8
52
+ cl.stop_bits.should == 1
53
+ cl.parity.should == SerialPort::NONE
54
+ end
55
+ end
56
+
57
+ it 'should have closed? method' do
58
+ @sp.should_receive(:closed?).and_return(false)
59
+ @cl.closed?.should == false
60
+
61
+ @sp.should_receive(:closed?).and_return(false)
62
+ @sp.should_receive(:close)
63
+
64
+ @cl.close
65
+
66
+ @sp.should_receive(:closed?).and_return(true)
67
+ @cl.closed?.should == true
68
+ end
69
+
70
+ it 'should give slave object in block' do
71
+ @cl.with_slave(1) do |slave|
72
+ slave.uid = 1
73
+ end
74
+ end
75
+
76
+ def stab_valid_request
77
+ request = "\x3\x0\x1\x0\x1"
78
+ @sp.should_receive(:write).with("\1#{request}\xd5\xca")
79
+ @sp.should_receive(:read).with(2).and_return("\x1\x3")
80
+ @sp.should_receive(:read).with(1).and_return("\x2")
81
+ @sp.should_receive(:read).with(4).and_return("\xff\xff\xb9\xf4")
82
+
83
+ request
84
+ end
85
+ end
86
+
@@ -0,0 +1,31 @@
1
+ # -*- coding: ascii
2
+ require 'rmodbus'
3
+
4
+ describe ModBus::RTUServer do
5
+ before do
6
+ @sp = double('SerialPort')
7
+ SerialPort.should_receive(:new).with('/dev/ttyS0', 4800, 7, 2, SerialPort::NONE).and_return(@sp)
8
+ @sp.stub(:read_timeout=)
9
+ @sp.should_receive(:flow_control=).with(SerialPort::NONE)
10
+
11
+ @server = ModBus::RTUServer.new('/dev/ttyS0', 4800, :data_bits => 7, :stop_bits => 2)
12
+ @slave = @server.with_slave(1)
13
+ @slave.coils = [1,0,1,1]
14
+ @slave.discrete_inputs = [1,1,0,0]
15
+ @slave.holding_registers = [1,2,3,4]
16
+ @slave.input_registers = [1,2,3,4]
17
+ end
18
+
19
+ it "should be valid initialized " do
20
+ @slave.coils.should == [1,0,1,1]
21
+ @slave.discrete_inputs.should == [1,1,0,0]
22
+ @slave.holding_registers.should == [1,2,3,4]
23
+ @slave.input_registers.should == [1,2,3,4]
24
+
25
+ @server.port.should == '/dev/ttyS0'
26
+ @server.baud.should == 4800
27
+ @server.data_bits.should == 7
28
+ @server.stop_bits.should == 2
29
+ @server.parity.should == SerialPort::NONE
30
+ end
31
+ end
@@ -0,0 +1,76 @@
1
+ # -*- coding: ascii
2
+ require 'rmodbus'
3
+
4
+ describe ModBus::RTUClient do
5
+ describe "method 'query'" do
6
+ before do
7
+ @sock = double('Socket')
8
+ Socket.should_receive(:tcp).with("127.0.0.1", 10002, nil, nil, hash_including(:connect_timeout)).and_return(@sock)
9
+ @sock.stub(:read_timeout=)
10
+ @sock.stub(:flush)
11
+
12
+ @cl = ModBus::RTUClient.new("127.0.0.1")
13
+ @slave = @cl.with_slave(1)
14
+ @slave.read_retries = 1
15
+ end
16
+
17
+ it "should ignore frame with other UID" do
18
+ request = "\x10\x0\x1\x0\x1\x2\xff\xff"
19
+ @sock.should_receive(:write).with("\1#{request}\xA6\x31")
20
+ @sock.should_receive(:read).with(2).and_return("\x2\x10")
21
+ @sock.should_receive(:read).with(6).and_return("\x0\x1\x0\x1\x1C\x08")
22
+ lambda {@slave.query(request)}.should raise_error(ModBus::Errors::ModBusTimeout)
23
+ end
24
+
25
+ it "should ignored frame with incorrect CRC" do
26
+ request = "\x10\x0\x1\x0\x1\x2\xff\xff"
27
+ @sock.should_receive(:write).with("\1#{request}\xA6\x31")
28
+ @sock.should_receive(:read).with(2).and_return("\x2\x10")
29
+ @sock.should_receive(:read).with(6).and_return("\x0\x1\x0\x1\x1C\x08")
30
+ lambda {@slave.query(request)}.should raise_error(ModBus::Errors::ModBusTimeout)
31
+ end
32
+
33
+ it "should return value of registers"do
34
+ request = "\x3\x0\x1\x0\x1"
35
+ @sock.should_receive(:write).with("\1#{request}\xd5\xca")
36
+ @sock.should_receive(:read).with(2).and_return("\x1\x3")
37
+ @sock.should_receive(:read).with(1).and_return("\x2")
38
+ @sock.should_receive(:read).with(4).and_return("\xff\xff\xb9\xf4")
39
+ @slave.query(request).should == "\xff\xff"
40
+ end
41
+
42
+ it 'should sugar connect method' do
43
+ ipaddr, port = '127.0.0.1', 502
44
+ Socket.should_receive(:tcp).with(ipaddr, port, nil, nil, hash_including(:connect_timeout)).and_return(@sock)
45
+ @sock.should_receive(:closed?).and_return(false)
46
+ @sock.should_receive(:close)
47
+ ModBus::RTUClient.connect(ipaddr, port) do |cl|
48
+ cl.ipaddr.should == ipaddr
49
+ cl.port.should == port
50
+ end
51
+ end
52
+
53
+ it 'should have closed? method' do
54
+ @sock.should_receive(:closed?).and_return(false)
55
+ @cl.closed?.should == false
56
+
57
+ @sock.should_receive(:closed?).and_return(false)
58
+ @sock.should_receive(:close)
59
+
60
+ @cl.close
61
+
62
+ @sock.should_receive(:closed?).and_return(true)
63
+ @cl.closed?.should == true
64
+ end
65
+
66
+ it 'should give slave object in block' do
67
+ @cl.with_slave(1) do |slave|
68
+ slave.uid = 1
69
+ end
70
+ end
71
+ end
72
+
73
+ it "should tune connection timeout" do
74
+ lambda { ModBus::RTUClient.new('81.123.231.11', 1999, :connect_timeout => 0.001) }.should raise_error(ModBus::Errors::ModBusTimeout)
75
+ end
76
+ end