rmodbus 1.0.0-java

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