rmodbus 0.5.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/NEWS.md +52 -0
- data/README.md +87 -0
- data/Rakefile +22 -36
- data/examples/perfomance_rtu.rb +35 -37
- data/examples/perfomance_tcp.rb +36 -38
- data/examples/use_rtu_via_tcp_modbus.rb +8 -5
- data/examples/use_tcp_modbus.rb +10 -6
- data/lib/rmodbus/client.rb +52 -174
- data/lib/rmodbus/common.rb +45 -18
- data/lib/rmodbus/{exceptions.rb → errors.rb} +3 -0
- data/lib/rmodbus/ext.rb +25 -2
- data/lib/rmodbus/proxy.rb +54 -0
- data/lib/rmodbus/{crc16.rb → rtu.rb} +73 -2
- data/lib/rmodbus/rtu_client.rb +20 -116
- data/lib/rmodbus/rtu_server.rb +28 -57
- data/lib/rmodbus/rtu_slave.rb +59 -0
- data/lib/rmodbus/rtu_via_tcp_client.rb +22 -86
- data/lib/rmodbus/rtu_via_tcp_server.rb +31 -95
- data/lib/rmodbus/rtu_via_tcp_slave.rb +58 -0
- data/lib/rmodbus/{parsers.rb → server.rb} +24 -15
- data/lib/rmodbus/slave.rb +268 -0
- data/lib/rmodbus/sp.rb +45 -0
- data/lib/rmodbus/tcp.rb +49 -0
- data/lib/rmodbus/tcp_client.rb +19 -88
- data/lib/rmodbus/tcp_server.rb +16 -19
- data/lib/rmodbus/tcp_slave.rb +64 -0
- data/lib/rmodbus/version.rb +17 -0
- data/lib/rmodbus.rb +20 -4
- data/spec/client_spec.rb +19 -45
- data/spec/exception_spec.rb +26 -27
- data/spec/ext_spec.rb +24 -1
- data/spec/logging_spec.rb +31 -37
- data/spec/proxy_spec.rb +73 -0
- data/spec/read_rtu_response_spec.rb +2 -4
- data/spec/rtu_client_spec.rb +17 -19
- data/spec/rtu_server_spec.rb +1 -3
- data/spec/rtu_via_tcp_client_spec.rb +69 -63
- data/spec/slave_spec.rb +55 -0
- data/spec/tcp_client_spec.rb +77 -69
- data/spec/tcp_server_spec.rb +34 -49
- metadata +123 -37
- data/AUTHORS +0 -3
- data/ChangeLog +0 -82
- data/LICENSE +0 -675
- data/README +0 -53
- data/examples/add_new_function.rb +0 -19
data/NEWS.md
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
Release 1.0.0
|
2
|
+
=====================================
|
3
|
+
New API for client part of library
|
4
|
+
---------------------------------------
|
5
|
+
|
6
|
+
Example:
|
7
|
+
|
8
|
+
require 'rmodbus'
|
9
|
+
|
10
|
+
ModBus::TCPClient.new('127.0.0.1', 8502) do |cl|
|
11
|
+
cl.with_slave(1) do |slave|
|
12
|
+
# Read a single holding register at address 16
|
13
|
+
slave.holding_registers[16]
|
14
|
+
|
15
|
+
# Write a single holding register at address 16
|
16
|
+
slave.holding_registers[16] = 123
|
17
|
+
|
18
|
+
# Read holding registers 16 through 20
|
19
|
+
slave.holding_registers[16..20]
|
20
|
+
|
21
|
+
# Write holding registers 16 through 20 with some values
|
22
|
+
slave.holding_registers[16..20] = [1, 2, 3, 4, 5]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
for more information [see](http://rdoc.info/gems/rmodbus/1.0.0/frames)
|
27
|
+
|
28
|
+
Conversion to/from 32bit registers
|
29
|
+
-----------------------------------
|
30
|
+
|
31
|
+
Some modbus devices use two registers to store 32bit values.
|
32
|
+
RModbus provides some helper functions to go back and forth between these two things when reading/writing.
|
33
|
+
The built-in examples assume registers in a particular order but it's trivial to change.
|
34
|
+
|
35
|
+
# Reading values in multiple registers (you can read more than 2 and convert them all so long as they are in multiples of 2)
|
36
|
+
res = slave.holding_registers[0..1]
|
37
|
+
res.inspect => [20342, 17344]
|
38
|
+
res.to_32i => [1136676726]
|
39
|
+
res.to_32f => [384.620788574219]
|
40
|
+
|
41
|
+
# Writing 32b values to multiple registers
|
42
|
+
cl.holding_registers[0..1] = [1136676726].from_32i
|
43
|
+
cl.holding_registers[0..1] => [20342, 17344]
|
44
|
+
cl.holding_registers[2..3] = [384.620788574219].from_32f
|
45
|
+
cl.holding_registers[2..3] => [20342, 17344]
|
46
|
+
|
47
|
+
Support JRuby
|
48
|
+
--------------------------------------
|
49
|
+
Now you could use RModBus on JRuby without RTU implementation.
|
50
|
+
|
51
|
+
RTU classes requires gem [serialport](https://github.com/hparra/ruby-serialport) which
|
52
|
+
currently not compatible with JRuby
|
data/README.md
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
RModBus
|
2
|
+
==========================
|
3
|
+
|
4
|
+
**RModBus** - free implementation of protocol ModBus.
|
5
|
+
|
6
|
+
Features
|
7
|
+
---------------------------
|
8
|
+
- Ruby 1.8, Ruby 1.9, JRuby (without serial ModBus RTU)
|
9
|
+
- TCP, RTU, RTU over TCP protocols
|
10
|
+
- Client(master) and server(slave)
|
11
|
+
- 16, 32 -bit and float registers
|
12
|
+
|
13
|
+
Support functions
|
14
|
+
---------------------------
|
15
|
+
* Read Coils (0x01)
|
16
|
+
* Read Discrete Inputs (0x02)
|
17
|
+
* Read Holding Registers (0x03)
|
18
|
+
* Read Input Registers (0x04)
|
19
|
+
* Write Single Coil (0x05)
|
20
|
+
* Write Single Register (0x06)
|
21
|
+
* Write Multiple Coils (0x0F)
|
22
|
+
* Write Multiple registers (0x10)
|
23
|
+
* Mask Write register (0x16)
|
24
|
+
|
25
|
+
Installation
|
26
|
+
------------------------------------
|
27
|
+
|
28
|
+
Download and install RModBus with the following
|
29
|
+
|
30
|
+
**$ gem install rmodbus**
|
31
|
+
|
32
|
+
Example
|
33
|
+
------------------------------------
|
34
|
+
|
35
|
+
require 'rmodbus'
|
36
|
+
|
37
|
+
ModBus::TCPClient.new('127.0.0.1', 8502) do |cl|
|
38
|
+
cl.with_slave(1) do |slave|
|
39
|
+
# Read a single holding register at address 16
|
40
|
+
slave.holding_registers[16]
|
41
|
+
|
42
|
+
# Write a single holding register at address 16
|
43
|
+
slave.holding_registers[16] = 123
|
44
|
+
|
45
|
+
# Read holding registers 16 through 20
|
46
|
+
slave.holding_registers[16..20]
|
47
|
+
|
48
|
+
# Write holding registers 16 through 20 with some values
|
49
|
+
slave.holding_registers[16..20] = [1, 2, 3, 4, 5]
|
50
|
+
end
|
51
|
+
end
|
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
|
+
GitHub
|
74
|
+
----------------------------------
|
75
|
+
|
76
|
+
You can checkout source code from GitHub repositry
|
77
|
+
|
78
|
+
**$ git clone git://github.com/flipback/RModBus.git**
|
79
|
+
|
80
|
+
Reference
|
81
|
+
----------------------------------
|
82
|
+
|
83
|
+
Home page: http://rmodbus.heroku.com
|
84
|
+
|
85
|
+
RModBud on GitHub: http://github.com/flipback/RModBus
|
86
|
+
|
87
|
+
ModBus community: http://www.modbus-ida.org
|
data/Rakefile
CHANGED
@@ -1,45 +1,31 @@
|
|
1
|
-
|
2
|
-
if RUBY_VERSION.to_f >= 1.9
|
3
|
-
require 'fileutils'
|
4
|
-
else
|
5
|
-
require 'ftools'
|
6
|
-
end
|
1
|
+
# encoding: utf-8
|
7
2
|
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
8
5
|
begin
|
9
|
-
|
10
|
-
rescue
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
11
|
end
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
t.rcov = false
|
13
|
+
require 'rake'
|
14
|
+
require 'rspec/core'
|
15
|
+
require 'rspec/core/rake_task'
|
16
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
17
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
18
|
+
if RUBY_PLATFORM == "java"
|
19
|
+
spec.pattern.exclude("spec/rtu_client_spec.rb", "spec/rtu_server_spec.rb")
|
21
20
|
end
|
22
|
-
rescue Exception
|
23
|
-
puts 'RSpec not available. Install it with: sudo gem install rspec'
|
24
21
|
end
|
25
22
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
sitedir = CONFIG['sitelibdir']
|
31
|
-
rmodbus_dest = File.join(sitedir, 'rmodbus')
|
32
|
-
|
33
|
-
File::makedirs(rmodbus_dest, true)
|
34
|
-
File::chmod(0755, rmodbus_dest)
|
23
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
24
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
25
|
+
spec.rcov = true
|
26
|
+
end
|
35
27
|
|
36
|
-
|
28
|
+
task :default => :spec
|
37
29
|
|
38
|
-
|
39
|
-
|
40
|
-
target_dir = File.join(sitedir, fn_dir)
|
41
|
-
File::makedirs(target_dir) unless File.exist?(target_dir)
|
42
|
-
File::install(File.join('lib', fn), File.join(sitedir, fn), 0644, true)
|
43
|
-
end
|
44
|
-
|
45
|
-
end
|
30
|
+
require 'yard'
|
31
|
+
YARD::Rake::YardocTask.new
|
data/examples/perfomance_rtu.rb
CHANGED
@@ -10,49 +10,47 @@ TIMES = 100
|
|
10
10
|
|
11
11
|
srv = RTUServer.new 'com3', BAUD
|
12
12
|
srv.coils = [0,1] * 50
|
13
|
-
srv.
|
13
|
+
srv.discrete_inputs = [1,0] * 50
|
14
14
|
srv.holding_registers = [0,1,2,3,4,5,6,7,8,9] * 10
|
15
15
|
srv.input_registers = [0,1,2,3,4,5,6,7,8,9] * 10
|
16
16
|
srv.start
|
17
17
|
|
18
18
|
|
19
|
-
cl = RTUClient.new
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
TIMES.times { cl.write_multiple_registers 0, [0,1,2,3,4,5,6,7,8,9] * 10 }
|
19
|
+
cl = RTUClient.new('com4', BAUD)
|
20
|
+
cl.with_slave(1) do |slave|
|
21
|
+
Benchmark.bmbm do |x|
|
22
|
+
x.report('Read coils') do
|
23
|
+
TIMES.times { slave.read_coils 0, 100 }
|
24
|
+
end
|
25
|
+
|
26
|
+
x.report('Read discrete inputs') do
|
27
|
+
TIMES.times { slave.read_discrete_inputs 0, 100 }
|
28
|
+
end
|
29
|
+
|
30
|
+
x.report('Read holding registers') do
|
31
|
+
TIMES.times { slave.read_holding_registers 0, 100 }
|
32
|
+
end
|
33
|
+
|
34
|
+
x.report('Read input registers') do
|
35
|
+
TIMES.times { slave.read_input_registers 0, 100 }
|
36
|
+
end
|
37
|
+
|
38
|
+
x.report('Write single coil') do
|
39
|
+
TIMES.times { slave.write_single_coil 0, 1 }
|
40
|
+
end
|
41
|
+
|
42
|
+
x.report('Write single register') do
|
43
|
+
TIMES.times { slave.write_single_register 100, 0xAAAA }
|
44
|
+
end
|
45
|
+
|
46
|
+
x.report('Write multiple coils') do
|
47
|
+
TIMES.times { slave.write_multiple_coils 0, [1,0] * 50 }
|
48
|
+
end
|
49
|
+
|
50
|
+
x.report('Write multiple registers') do
|
51
|
+
TIMES.times { slave.write_multiple_registers 0, [0,1,2,3,4,5,6,7,8,9] * 10 }
|
52
|
+
end
|
54
53
|
end
|
55
54
|
end
|
56
|
-
|
57
55
|
srv.stop
|
58
56
|
cl.close
|
data/examples/perfomance_tcp.rb
CHANGED
@@ -9,49 +9,47 @@ TIMES = 1000
|
|
9
9
|
|
10
10
|
srv = ModBus::TCPServer.new 1502
|
11
11
|
srv.coils = [0,1] * 50
|
12
|
-
srv.
|
12
|
+
srv.discrete_inputs = [1,0] * 50
|
13
13
|
srv.holding_registers = [0,1,2,3,4,5,6,7,8,9] * 10
|
14
14
|
srv.input_registers = [0,1,2,3,4,5,6,7,8,9] * 10
|
15
15
|
srv.start
|
16
16
|
|
17
17
|
|
18
|
-
cl = TCPClient.new
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
TIMES.times { cl.write_multiple_registers 0, [0,1,2,3,4,5,6,7,8,9] * 10 }
|
18
|
+
cl = TCPClient.new('127.0.0.1', 1502)
|
19
|
+
cl.with_slave(1) do |slave|
|
20
|
+
Benchmark.bmbm do |x|
|
21
|
+
x.report('Read coils') do
|
22
|
+
TIMES.times { slave.read_coils 0, 100 }
|
23
|
+
end
|
24
|
+
|
25
|
+
x.report('Read discrete inputs') do
|
26
|
+
TIMES.times { slave.read_discrete_inputs 0, 100 }
|
27
|
+
end
|
28
|
+
|
29
|
+
x.report('Read holding registers') do
|
30
|
+
TIMES.times { slave.read_holding_registers 0, 100 }
|
31
|
+
end
|
32
|
+
|
33
|
+
x.report('Read input registers') do
|
34
|
+
TIMES.times { slave.read_input_registers 0, 100 }
|
35
|
+
end
|
36
|
+
|
37
|
+
x.report('Write single coil') do
|
38
|
+
TIMES.times { slave.write_single_coil 0, 1 }
|
39
|
+
end
|
40
|
+
|
41
|
+
x.report('Write single register') do
|
42
|
+
TIMES.times { slave.write_single_register 100, 0xAAAA }
|
43
|
+
end
|
44
|
+
|
45
|
+
x.report('Write multiple coils') do
|
46
|
+
TIMES.times { slave.write_multiple_coils 0, [1,0] * 50 }
|
47
|
+
end
|
48
|
+
|
49
|
+
x.report('Write multiple registers') do
|
50
|
+
TIMES.times { slave.write_multiple_registers 0, [0,1,2,3,4,5,6,7,8,9] * 10 }
|
51
|
+
end
|
53
52
|
end
|
54
53
|
end
|
55
|
-
|
56
|
-
srv.stop
|
57
54
|
cl.close
|
55
|
+
srv.stop
|
@@ -9,11 +9,14 @@ srv.input_registers = [1,2,3,4]
|
|
9
9
|
srv.debug = true
|
10
10
|
srv.start
|
11
11
|
|
12
|
-
ModBus::RTUViaTCPClient.connect('127.0.0.1', 10002
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
ModBus::RTUViaTCPClient.connect('127.0.0.1', 10002) do |cl|
|
13
|
+
cl.with_slave(1) do |slave|
|
14
|
+
slave.debug = true
|
15
|
+
regs = slave.holding_registers
|
16
|
+
puts regs[0..3]
|
17
|
+
regs[0..3] = [2,0,1,1]
|
18
|
+
puts regs[0..3]
|
19
|
+
end
|
17
20
|
end
|
18
21
|
|
19
22
|
srv.shutdown
|
data/examples/use_tcp_modbus.rb
CHANGED
@@ -3,17 +3,21 @@ require 'rmodbus'
|
|
3
3
|
|
4
4
|
srv = ModBus::TCPServer.new(8502,1)
|
5
5
|
srv.coils = [1,0,1,1]
|
6
|
-
srv.
|
6
|
+
srv.discrete_inputs = [1,1,0,0]
|
7
7
|
srv.holding_registers = [1,2,3,4]
|
8
8
|
srv.input_registers = [1,2,3,4]
|
9
9
|
srv.debug = true
|
10
10
|
srv.audit = true
|
11
11
|
srv.start
|
12
12
|
|
13
|
-
ModBus::TCPClient.connect('127.0.0.1', 8502
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
ModBus::TCPClient.connect('127.0.0.1', 8502) do |cl|
|
14
|
+
cl.with_slave(1) do |slave|
|
15
|
+
slave.debug = true
|
16
|
+
regs = slave.holding_registers
|
17
|
+
puts regs[0..3]
|
18
|
+
regs[0..3] = [2,0,1,1]
|
19
|
+
puts regs[0..3]
|
20
|
+
end
|
18
21
|
end
|
22
|
+
|
19
23
|
srv.stop
|