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.
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