rmodbus 1.0.0-java
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 +31 -0
- data/examples/perfomance_rtu.rb +56 -0
- data/examples/perfomance_tcp.rb +55 -0
- data/examples/use_rtu_via_tcp_modbus.rb +22 -0
- data/examples/use_tcp_modbus.rb +23 -0
- data/lib/rmodbus/client.rb +91 -0
- data/lib/rmodbus/common.rb +50 -0
- data/lib/rmodbus/errors.rb +50 -0
- data/lib/rmodbus/ext.rb +88 -0
- data/lib/rmodbus/proxy.rb +54 -0
- data/lib/rmodbus/rtu.rb +140 -0
- data/lib/rmodbus/rtu_client.rb +41 -0
- data/lib/rmodbus/rtu_server.rb +61 -0
- data/lib/rmodbus/rtu_slave.rb +59 -0
- data/lib/rmodbus/rtu_via_tcp_client.rb +41 -0
- data/lib/rmodbus/rtu_via_tcp_server.rb +50 -0
- data/lib/rmodbus/rtu_via_tcp_slave.rb +58 -0
- data/lib/rmodbus/server.rb +142 -0
- 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 +39 -0
- data/lib/rmodbus/tcp_server.rb +61 -0
- data/lib/rmodbus/tcp_slave.rb +64 -0
- data/lib/rmodbus/version.rb +17 -0
- data/lib/rmodbus.rb +35 -0
- data/spec/client_spec.rb +31 -0
- data/spec/exception_spec.rb +116 -0
- data/spec/ext_spec.rb +46 -0
- data/spec/logging_spec.rb +65 -0
- data/spec/proxy_spec.rb +73 -0
- data/spec/read_rtu_response_spec.rb +91 -0
- data/spec/rtu_client_spec.rb +74 -0
- data/spec/rtu_server_spec.rb +29 -0
- data/spec/rtu_via_tcp_client_spec.rb +78 -0
- data/spec/slave_spec.rb +55 -0
- data/spec/tcp_client_spec.rb +81 -0
- data/spec/tcp_server_spec.rb +99 -0
- metadata +165 -0
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
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
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
|
+
end
|
12
|
+
|
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")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
24
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
25
|
+
spec.rcov = true
|
26
|
+
end
|
27
|
+
|
28
|
+
task :default => :spec
|
29
|
+
|
30
|
+
require 'yard'
|
31
|
+
YARD::Rake::YardocTask.new
|
@@ -0,0 +1,56 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__),'../lib')
|
2
|
+
|
3
|
+
require 'rmodbus'
|
4
|
+
require 'benchmark'
|
5
|
+
|
6
|
+
include ModBus
|
7
|
+
|
8
|
+
BAUD = 9600
|
9
|
+
TIMES = 100
|
10
|
+
|
11
|
+
srv = RTUServer.new 'com3', BAUD
|
12
|
+
srv.coils = [0,1] * 50
|
13
|
+
srv.discrete_inputs = [1,0] * 50
|
14
|
+
srv.holding_registers = [0,1,2,3,4,5,6,7,8,9] * 10
|
15
|
+
srv.input_registers = [0,1,2,3,4,5,6,7,8,9] * 10
|
16
|
+
srv.start
|
17
|
+
|
18
|
+
|
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
|
53
|
+
end
|
54
|
+
end
|
55
|
+
srv.stop
|
56
|
+
cl.close
|
@@ -0,0 +1,55 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__),'../lib')
|
2
|
+
|
3
|
+
require 'rmodbus'
|
4
|
+
require 'benchmark'
|
5
|
+
|
6
|
+
include ModBus
|
7
|
+
|
8
|
+
TIMES = 1000
|
9
|
+
|
10
|
+
srv = ModBus::TCPServer.new 1502
|
11
|
+
srv.coils = [0,1] * 50
|
12
|
+
srv.discrete_inputs = [1,0] * 50
|
13
|
+
srv.holding_registers = [0,1,2,3,4,5,6,7,8,9] * 10
|
14
|
+
srv.input_registers = [0,1,2,3,4,5,6,7,8,9] * 10
|
15
|
+
srv.start
|
16
|
+
|
17
|
+
|
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
|
52
|
+
end
|
53
|
+
end
|
54
|
+
cl.close
|
55
|
+
srv.stop
|
@@ -0,0 +1,22 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib/')
|
2
|
+
require 'rmodbus'
|
3
|
+
|
4
|
+
srv = ModBus::RTUViaTCPServer.new(10002,1)
|
5
|
+
srv.coils = [1,0,1,1]
|
6
|
+
srv.discrete_inputs = [1,1,0,0]
|
7
|
+
srv.holding_registers = [1,2,3,4]
|
8
|
+
srv.input_registers = [1,2,3,4]
|
9
|
+
srv.debug = true
|
10
|
+
srv.start
|
11
|
+
|
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
|
20
|
+
end
|
21
|
+
|
22
|
+
srv.shutdown
|
@@ -0,0 +1,23 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib/')
|
2
|
+
require 'rmodbus'
|
3
|
+
|
4
|
+
srv = ModBus::TCPServer.new(8502,1)
|
5
|
+
srv.coils = [1,0,1,1]
|
6
|
+
srv.discrete_inputs = [1,1,0,0]
|
7
|
+
srv.holding_registers = [1,2,3,4]
|
8
|
+
srv.input_registers = [1,2,3,4]
|
9
|
+
srv.debug = true
|
10
|
+
srv.audit = true
|
11
|
+
srv.start
|
12
|
+
|
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
|
21
|
+
end
|
22
|
+
|
23
|
+
srv.stop
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# RModBus - free implementation of ModBus protocol on Ruby.
|
2
|
+
#
|
3
|
+
# Copyright (C) 2008-2011 Timin Aleksey
|
4
|
+
#
|
5
|
+
# This program is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
|
15
|
+
module ModBus
|
16
|
+
# @abstract
|
17
|
+
class Client
|
18
|
+
include Errors
|
19
|
+
|
20
|
+
# Initialized client (alias :connect)
|
21
|
+
# @example
|
22
|
+
# Client.new(any_args) do |client|
|
23
|
+
# client.closed? #=> false
|
24
|
+
# end
|
25
|
+
# @param *args depends on implementation
|
26
|
+
# @yield return client object and close it before exit
|
27
|
+
# @return [Client] client object
|
28
|
+
def initialize(*args, &block)
|
29
|
+
@io = open_connection(*args)
|
30
|
+
if block_given?
|
31
|
+
yield self
|
32
|
+
close
|
33
|
+
else
|
34
|
+
self
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class << self
|
39
|
+
alias_method :connect, :new
|
40
|
+
end
|
41
|
+
|
42
|
+
# Given slave object
|
43
|
+
# @example
|
44
|
+
# cl = Client.new
|
45
|
+
# cl.with_slave(1) do |slave|
|
46
|
+
# slave.holding_registers[0..100]
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# @param [Integer, #read] uid slave devise
|
50
|
+
# @return [Slave] slave object
|
51
|
+
def with_slave(uid, &block)
|
52
|
+
slave = get_slave(uid, @io)
|
53
|
+
if block_given?
|
54
|
+
yield slave
|
55
|
+
else
|
56
|
+
slave
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Check connections
|
61
|
+
# @return [Boolean]
|
62
|
+
def closed?
|
63
|
+
@io.closed?
|
64
|
+
end
|
65
|
+
|
66
|
+
# Close connections
|
67
|
+
def close
|
68
|
+
@io.close unless @io.closed?
|
69
|
+
end
|
70
|
+
|
71
|
+
protected
|
72
|
+
def open_connection(*args)
|
73
|
+
#Stub conn object
|
74
|
+
@io = Object.new
|
75
|
+
|
76
|
+
@io.instance_eval """
|
77
|
+
def close
|
78
|
+
end
|
79
|
+
|
80
|
+
def closed?
|
81
|
+
true
|
82
|
+
end
|
83
|
+
"""
|
84
|
+
@io
|
85
|
+
end
|
86
|
+
|
87
|
+
def get_slave(uid,io)
|
88
|
+
Slave.new(uid, io)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# RModBus - free implementation of ModBus protocol in Ruby.
|
2
|
+
#
|
3
|
+
# Copyright (C) 2010 Timin Aleksey
|
4
|
+
# Copyright (C) 2010 Kelley Reynolds
|
5
|
+
#
|
6
|
+
# This program is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU General Public License for more details.
|
15
|
+
|
16
|
+
module ModBus
|
17
|
+
module Common
|
18
|
+
# @return [Boolean] debug mode
|
19
|
+
# default false
|
20
|
+
attr_accessor :debug
|
21
|
+
|
22
|
+
@debug = false
|
23
|
+
|
24
|
+
private
|
25
|
+
# Put log message on standart output
|
26
|
+
# @param [String] msg message for log
|
27
|
+
def log(msg)
|
28
|
+
$stdout.puts msg if @debug
|
29
|
+
end
|
30
|
+
|
31
|
+
# Convert string of byte to string for log
|
32
|
+
# @example
|
33
|
+
# logging_bytes("\x1\xa\x8") => "[01][0a][08]"
|
34
|
+
# @param [String] msg input string
|
35
|
+
# @return [String] readable string of bytes
|
36
|
+
def logging_bytes(msg)
|
37
|
+
result = ""
|
38
|
+
msg.each_byte do |c|
|
39
|
+
byte = if c < 16
|
40
|
+
'0' + c.to_s(16)
|
41
|
+
else
|
42
|
+
c.to_s(16)
|
43
|
+
end
|
44
|
+
result << "[#{byte}]"
|
45
|
+
end
|
46
|
+
result
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# RModBus - free implementation of ModBus protocol on Ruby.
|
2
|
+
#
|
3
|
+
# Copyright (C) 2008 Timin Aleksey
|
4
|
+
#
|
5
|
+
# This program is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
module ModBus
|
15
|
+
|
16
|
+
module Errors
|
17
|
+
|
18
|
+
class ProxyException < StandardError
|
19
|
+
end
|
20
|
+
|
21
|
+
class ModBusException < StandardError
|
22
|
+
end
|
23
|
+
|
24
|
+
class IllegalFunction < ModBusException
|
25
|
+
end
|
26
|
+
|
27
|
+
class IllegalDataAddress < ModBusException
|
28
|
+
end
|
29
|
+
|
30
|
+
class IllegalDataValue < ModBusException
|
31
|
+
end
|
32
|
+
|
33
|
+
class SlaveDeviceFailure < ModBusException
|
34
|
+
end
|
35
|
+
|
36
|
+
class Acknowledge < ModBusException
|
37
|
+
end
|
38
|
+
|
39
|
+
class SlaveDeviceBus < ModBusException
|
40
|
+
end
|
41
|
+
|
42
|
+
class MemoryParityError < ModBusException
|
43
|
+
end
|
44
|
+
|
45
|
+
class ModBusTimeout < ModBusException
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
data/lib/rmodbus/ext.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
# RModBus - free implementation of ModBus protocol on Ruby.
|
2
|
+
#
|
3
|
+
# Copyright (C) 2009 Timin Aleksey
|
4
|
+
#
|
5
|
+
# This program is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
|
15
|
+
class String
|
16
|
+
|
17
|
+
unless RUBY_VERSION =~ /^1\.9/
|
18
|
+
def getbyte(index)
|
19
|
+
self[index].to_i
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def unpack_bits
|
24
|
+
array_bit = []
|
25
|
+
self.unpack('b*')[0].each_char do |c|
|
26
|
+
array_bit << c.to_i
|
27
|
+
end
|
28
|
+
array_bit
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
class Integer
|
34
|
+
|
35
|
+
# Shortcut or turning an integer into a word
|
36
|
+
def to_word
|
37
|
+
[self].pack('n')
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
class Array
|
43
|
+
|
44
|
+
# Given an array of 16bit Fixnum, we turn it into 32bit Int in big-endian order, halving the size
|
45
|
+
def to_32f
|
46
|
+
raise "Array requires an even number of elements to pack to 32bits: was #{self.size}" unless self.size.even?
|
47
|
+
self.each_slice(2).map { |(lsb, msb)| [msb, lsb].pack('n*').unpack('g')[0] }
|
48
|
+
end
|
49
|
+
|
50
|
+
# Given an array of 32bit Floats, we turn it into an array of 16bit Fixnums, doubling the size
|
51
|
+
def from_32f
|
52
|
+
self.pack('g*').unpack('n*').each_slice(2).map { |arr| arr.reverse }.flatten
|
53
|
+
end
|
54
|
+
|
55
|
+
# Given an array of 16bit Fixnum, we turn it into 32bit Float in big-endian order, halving the size
|
56
|
+
def to_32i
|
57
|
+
raise "Array requires an even number of elements to pack to 32bits: was #{self.size}" unless self.size.even?
|
58
|
+
self.each_slice(2).map { |(lsb, msb)| [msb, lsb].pack('n*').unpack('N')[0] }
|
59
|
+
end
|
60
|
+
|
61
|
+
# Given an array of 32bit Fixnum, we turn it into an array of 16bit fixnums, doubling the size
|
62
|
+
def from_32i
|
63
|
+
self.pack('N*').unpack('n*').each_slice(2).map { |arr| arr.reverse }.flatten
|
64
|
+
end
|
65
|
+
|
66
|
+
def pack_to_word
|
67
|
+
word = 0
|
68
|
+
s = ""
|
69
|
+
mask = 0x01
|
70
|
+
|
71
|
+
self.each do |bit|
|
72
|
+
word |= mask if bit > 0
|
73
|
+
mask <<= 1
|
74
|
+
if mask == 0x100
|
75
|
+
mask = 0x01
|
76
|
+
s << word.chr
|
77
|
+
word = 0
|
78
|
+
end
|
79
|
+
end
|
80
|
+
unless mask == 0x01
|
81
|
+
s << word.chr
|
82
|
+
else
|
83
|
+
s
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# ReadOnly and ReadWrite hash interface for modbus registers and coils
|
2
|
+
#
|
3
|
+
# Copyright (C) 2010 Kelley Reynolds
|
4
|
+
#
|
5
|
+
# This program is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
module ModBus
|
15
|
+
# Given a slave and a type of operation, execute a single or multiple read using hash syntax
|
16
|
+
class ReadOnlyProxy
|
17
|
+
# Initialize a proxy for a slave and a type of operation
|
18
|
+
def initialize(slave, type)
|
19
|
+
@slave, @type = slave, type
|
20
|
+
end
|
21
|
+
|
22
|
+
# Read single or multiple values from a modbus slave depending on whether a Fixnum or a Range was given.
|
23
|
+
# Note that in the case of multiples, a pluralized version of the method is sent to the slave
|
24
|
+
def [](key)
|
25
|
+
if key.instance_of?(Fixnum)
|
26
|
+
@slave.send("read_#{@type}", key, 1)
|
27
|
+
elsif key.instance_of?(Range)
|
28
|
+
@slave.send("read_#{@type}s", key.first, key.count)
|
29
|
+
else
|
30
|
+
raise ProxyException, "Invalid argument, must be integer or range. Was #{key.class}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class ReadWriteProxy < ReadOnlyProxy
|
36
|
+
# Write single or multiple values to a modbus slave depending on whether a Fixnum or a Range was given.
|
37
|
+
# Note that in the case of multiples, a pluralized version of the method is sent to the slave. Also when
|
38
|
+
# writing multiple values, the number of elements must match the number of registers in the range or an exception is raised
|
39
|
+
def []=(key, val)
|
40
|
+
if key.instance_of?(Fixnum)
|
41
|
+
@slave.send("write_#{@type}", key, val)
|
42
|
+
elsif key.instance_of?(Range)
|
43
|
+
if key.count != val.size
|
44
|
+
raise ProxyException, "The size of the range must match the size of the values (#{key.count} != #{val.size})"
|
45
|
+
end
|
46
|
+
|
47
|
+
@slave.send("write_#{@type}s", key.first, val)
|
48
|
+
else
|
49
|
+
raise ProxyException, "Invalid argument, must be integer or range. Was #{key.class}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|