rmodbus 0.5.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/NEWS.md +52 -0
  2. data/README.md +87 -0
  3. data/Rakefile +22 -36
  4. data/examples/perfomance_rtu.rb +35 -37
  5. data/examples/perfomance_tcp.rb +36 -38
  6. data/examples/use_rtu_via_tcp_modbus.rb +8 -5
  7. data/examples/use_tcp_modbus.rb +10 -6
  8. data/lib/rmodbus/client.rb +52 -174
  9. data/lib/rmodbus/common.rb +45 -18
  10. data/lib/rmodbus/{exceptions.rb → errors.rb} +3 -0
  11. data/lib/rmodbus/ext.rb +25 -2
  12. data/lib/rmodbus/proxy.rb +54 -0
  13. data/lib/rmodbus/{crc16.rb → rtu.rb} +73 -2
  14. data/lib/rmodbus/rtu_client.rb +20 -116
  15. data/lib/rmodbus/rtu_server.rb +28 -57
  16. data/lib/rmodbus/rtu_slave.rb +59 -0
  17. data/lib/rmodbus/rtu_via_tcp_client.rb +22 -86
  18. data/lib/rmodbus/rtu_via_tcp_server.rb +31 -95
  19. data/lib/rmodbus/rtu_via_tcp_slave.rb +58 -0
  20. data/lib/rmodbus/{parsers.rb → server.rb} +24 -15
  21. data/lib/rmodbus/slave.rb +268 -0
  22. data/lib/rmodbus/sp.rb +45 -0
  23. data/lib/rmodbus/tcp.rb +49 -0
  24. data/lib/rmodbus/tcp_client.rb +19 -88
  25. data/lib/rmodbus/tcp_server.rb +16 -19
  26. data/lib/rmodbus/tcp_slave.rb +64 -0
  27. data/lib/rmodbus/version.rb +17 -0
  28. data/lib/rmodbus.rb +20 -4
  29. data/spec/client_spec.rb +19 -45
  30. data/spec/exception_spec.rb +26 -27
  31. data/spec/ext_spec.rb +24 -1
  32. data/spec/logging_spec.rb +31 -37
  33. data/spec/proxy_spec.rb +73 -0
  34. data/spec/read_rtu_response_spec.rb +2 -4
  35. data/spec/rtu_client_spec.rb +17 -19
  36. data/spec/rtu_server_spec.rb +1 -3
  37. data/spec/rtu_via_tcp_client_spec.rb +69 -63
  38. data/spec/slave_spec.rb +55 -0
  39. data/spec/tcp_client_spec.rb +77 -69
  40. data/spec/tcp_server_spec.rb +34 -49
  41. metadata +123 -37
  42. data/AUTHORS +0 -3
  43. data/ChangeLog +0 -82
  44. data/LICENSE +0 -675
  45. data/README +0 -53
  46. data/examples/add_new_function.rb +0 -19
@@ -1,6 +1,6 @@
1
1
  # RModBus - free implementation of ModBus protocol on Ruby.
2
2
  #
3
- # Copyright (C) 2008 Timin Aleksey
3
+ # Copyright (C) 2008-2011 Timin Aleksey
4
4
  #
5
5
  # This program is free software: you can redistribute it and/or modify
6
6
  # it under the terms of the GNU General Public License as published by
@@ -12,202 +12,80 @@
12
12
  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
13
  # GNU General Public License for more details.
14
14
 
15
- require 'rmodbus/common'
16
- require 'rmodbus/exceptions'
17
- require 'rmodbus/ext'
18
-
19
-
20
15
  module ModBus
16
+ # @abstract
21
17
  class Client
22
18
  include Errors
23
- include Common
24
- # Number of times to retry on connection and read timeouts
25
- attr_accessor :read_retries, :read_retry_timeout
26
-
27
- def connection_retries
28
- warn "[DEPRECATION] `connection_retries` is deprecated. Please don't use it."
29
- @connection_retries
30
- end
31
-
32
- def connection_retries=(value)
33
- warn "[DEPRECATION] `connection_retries=` is deprecated. Please don't use it."
34
- @connection_retries = value
35
- end
36
-
37
19
 
38
- Exceptions = {
39
- 1 => IllegalFunction.new("The function code received in the query is not an allowable action for the server"),
40
- 2 => IllegalDataAddress.new("The data address received in the query is not an allowable address for the server"),
41
- 3 => IllegalDataValue.new("A value contained in the query data field is not an allowable value for server"),
42
- 4 => SlaveDeviceFailure.new("An unrecoverable error occurred while the server was attempting to perform the requested action"),
43
- 5 => Acknowledge.new("The server has accepted the request and is processing it, but a long duration of time will be required to do so"),
44
- 6 => SlaveDeviceBus.new("The server is engaged in processing a long duration program command"),
45
- 8 => MemoryParityError.new("The extended file area failed to pass a consistency check")
46
- }
47
- def initialize
48
- @connection_retries = 10
49
- @read_retries = 10
50
- @read_retry_timeout = 1
51
- end
52
- # Read value *ncoils* coils starting with *addr*
53
- #
54
- # Return array of their values
55
- def read_coils(addr, ncoils)
56
- query("\x1" + addr.to_word + ncoils.to_word).unpack_bits[0..ncoils-1]
57
- end
58
-
59
- # Read value *ncoils* discrete inputs starting with *addr*
60
- #
61
- # Return array of their values
62
- def read_discrete_inputs(addr, ncoils)
63
- query("\x2" + addr.to_word + ncoils.to_word).unpack_bits[0..ncoils-1]
64
- end
65
-
66
- # Deprecated version of read_discrete_inputs
67
- def read_discret_inputs(addr, ncoils)
68
- #warn "[DEPRECATION] `read_discret_inputs` is deprecated. Please use `read_discrete_inputs` instead."
69
- read_discrete_inputs(addr, ncoils)
70
- end
71
-
72
- # Read value *nreg* holding registers starting with *addr*
73
- #
74
- # Return array of their values
75
- def read_holding_registers(addr, nreg)
76
- query("\x3" + addr.to_word + nreg.to_word).unpack('n*')
77
- end
78
-
79
- # Read value *nreg* input registers starting with *addr*
80
- #
81
- # Return array of their values
82
- def read_input_registers(addr, nreg)
83
- query("\x4" + addr.to_word + nreg.to_word).unpack('n*')
84
- end
85
-
86
- # Write *val* in *addr* coil
87
- #
88
- # if *val* lager 0 write 1
89
- #
90
- # Return self
91
- def write_single_coil(addr, val)
92
- if val == 0
93
- query("\x5" + addr.to_word + 0.to_word)
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
94
33
  else
95
- query("\x5" + addr.to_word + 0xff00.to_word)
34
+ self
96
35
  end
97
- self
98
36
  end
99
37
 
100
- # Write *val* in *addr* register
101
- #
102
- # Return self
103
- def write_single_register(addr, val)
104
- query("\x6" + addr.to_word + val.to_word)
105
- self
38
+ class << self
39
+ alias_method :connect, :new
106
40
  end
107
41
 
108
- # Write *val* in coils starting with *addr*
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
109
48
  #
110
- # *val* it is array of bits
111
- #
112
- # Return self
113
- def write_multiple_coils(addr, val)
114
- nbyte = ((val.size-1) >> 3) + 1
115
- sum = 0
116
- (val.size - 1).downto(0) do |i|
117
- sum = sum << 1
118
- sum |= 1 if val[i] > 0
119
- end
120
-
121
- s_val = ""
122
- nbyte.times do
123
- s_val << (sum & 0xff).chr
124
- sum >>= 8
125
- end
126
-
127
- query("\xf" + addr.to_word + val.size.to_word + nbyte.chr + s_val)
128
- self
129
- end
130
-
131
- # Write *val* in registers starting with *addr*
132
- #
133
- # *val* it is array of integer
134
- #
135
- # Return self
136
- def write_multiple_registers(addr, val)
137
- s_val = ""
138
- val.each do |reg|
139
- s_val << reg.to_word
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
140
57
  end
141
-
142
- query("\x10" + addr.to_word + val.size.to_word + (val.size * 2).chr + s_val)
143
- self
144
58
  end
145
59
 
146
- # Write *current value & and_mask | or mask in *addr* register
147
- #
148
- # Return self
149
- def mask_write_register(addr, and_mask, or_mask)
150
- query("\x16" + addr.to_word + and_mask.to_word + or_mask.to_word)
151
- self
60
+ # Check connections
61
+ # @return [Boolean]
62
+ def closed?
63
+ @io.closed?
152
64
  end
153
65
 
154
- def query(pdu)
155
- tried = 0
156
- begin
157
- timeout(@read_retry_timeout, ModBusTimeout) do
158
- send_pdu(pdu)
159
- pdu = read_pdu
160
- end
161
- rescue ModBusTimeout => err
162
- log "Timeout of read operation: (#{@read_retries - tried})"
163
- tried += 1
164
- retry unless tried >= @read_retries
165
- raise ModBusTimeout.new, "Timed out during read attempt"
166
- end
167
-
168
- return nil if pdu.size == 0
169
-
170
- if pdu.getbyte(0) >= 0x80
171
- exc_id = pdu.getbyte(1)
172
- raise Exceptions[exc_id] unless Exceptions[exc_id].nil?
173
-
174
- raise ModBusException.new, "Unknown error"
175
- end
176
- pdu[2..-1]
66
+ # Close connections
67
+ def close
68
+ @io.close unless @io.closed?
177
69
  end
178
70
 
179
71
  protected
72
+ def open_connection(*args)
73
+ #Stub conn object
74
+ @io = Object.new
180
75
 
181
- def send_pdu(pdu)
182
- end
183
-
184
- def read_pdu
185
- end
76
+ @io.instance_eval """
77
+ def close
78
+ end
186
79
 
187
- def close
80
+ def closed?
81
+ true
82
+ end
83
+ """
84
+ @io
188
85
  end
189
86
 
190
- # We have to read specific amounts of numbers of bytes from the network depending on the function code and content
191
- def read_rtu_response(io)
192
- # Read the slave_id and function code
193
- msg = io.read(2)
194
- function_code = msg.getbyte(1)
195
- case function_code
196
- when 1,2,3,4 then
197
- # read the third byte to find out how much more
198
- # we need to read + CRC
199
- msg += io.read(1)
200
- msg += io.read(msg.getbyte(2)+2)
201
- when 5,6,15,16 then
202
- # We just read in an additional 6 bytes
203
- msg += io.read(6)
204
- when 22 then
205
- msg += io.read(8)
206
- when 0x80..0xff then
207
- msg += io.read(4)
208
- else
209
- raise ModBus::Errors::IllegalFunction, "Illegal function: #{function_code}"
210
- end
87
+ def get_slave(uid,io)
88
+ Slave.new(uid, io)
211
89
  end
212
90
  end
213
91
  end
@@ -1,23 +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
+
1
16
  module ModBus
2
- module Common
17
+ module Common
18
+ # @return [Boolean] debug mode
19
+ # default false
20
+ attr_accessor :debug
21
+
22
+ @debug = false
3
23
 
4
- private
5
- def log(msg)
6
- $stdout.puts msg if @debug
7
- end
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
8
30
 
9
- def logging_bytes(msg)
10
- result = ""
11
- msg.each_byte do |c|
12
- byte = if c < 16
13
- '0' + c.to_s(16)
14
- else
15
- c.to_s(16)
16
- end
17
- result << "[#{byte}]"
18
- end
19
- result
20
- end
21
- end
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
22
49
  end
23
50
 
@@ -15,6 +15,9 @@ module ModBus
15
15
 
16
16
  module Errors
17
17
 
18
+ class ProxyException < StandardError
19
+ end
20
+
18
21
  class ModBusException < StandardError
19
22
  end
20
23
 
data/lib/rmodbus/ext.rb CHANGED
@@ -27,19 +27,42 @@ class String
27
27
  end
28
28
  array_bit
29
29
  end
30
-
30
+
31
31
  end
32
32
 
33
33
  class Integer
34
34
 
35
+ # Shortcut or turning an integer into a word
35
36
  def to_word
36
- (self >> 8).chr + (self & 0xff).chr
37
+ [self].pack('n')
37
38
  end
38
39
 
39
40
  end
40
41
 
41
42
  class Array
42
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
+
43
66
  def pack_to_word
44
67
  word = 0
45
68
  s = ""
@@ -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
@@ -1,6 +1,7 @@
1
1
  # RModBus - free implementation of ModBus protocol in Ruby.
2
2
  #
3
- # Copyright (C) 2010 Timin Aleksey
3
+ # Copyright (C) 2010 - 2011 Timin Aleksey
4
+ # Copyright (C) 2010 Kelley Reynolds
4
5
  #
5
6
  # This program is free software: you can redistribute it and/or modify
6
7
  # it under the terms of the GNU General Public License as published by
@@ -13,7 +14,75 @@
13
14
  # GNU General Public License for more details.
14
15
 
15
16
  module ModBus
16
- module CRC16
17
+ module RTU
18
+ private
19
+
20
+ # We have to read specific amounts of numbers of bytes from the network depending on the function code and content
21
+ def read_rtu_response(io)
22
+ # Read the slave_id and function code
23
+ msg = io.read(2)
24
+ function_code = msg.getbyte(1)
25
+ case function_code
26
+ when 1,2,3,4 then
27
+ # read the third byte to find out how much more
28
+ # we need to read + CRC
29
+ msg += io.read(1)
30
+ msg += io.read(msg.getbyte(2)+2)
31
+ when 5,6,15,16 then
32
+ # We just read in an additional 6 bytes
33
+ msg += io.read(6)
34
+ when 22 then
35
+ msg += io.read(8)
36
+ when 0x80..0xff then
37
+ msg += io.read(4)
38
+ else
39
+ raise ModBus::Errors::IllegalFunction, "Illegal function: #{function_code}"
40
+ end
41
+ end
42
+
43
+ def read_rtu_request(io)
44
+ # Read the slave_id and function code
45
+ msg = io.read(2)
46
+
47
+ # If msg is nil, then our client never sent us anything and it's time to disconnect
48
+ return if msg.nil?
49
+
50
+ function_code = msg.getbyte(1)
51
+ if [1, 2, 3, 4, 5, 6].include?(function_code)
52
+ # read 6 more bytes and return the message total message
53
+ msg += io.read(6)
54
+ elsif [15, 16].include?(function_code)
55
+ # Read in first register, register count, and data bytes
56
+ msg += io.read(5)
57
+ # Read in however much data we need to + 2 CRC bytes
58
+ msg += io.read(msg.getbyte(6) + 2)
59
+ else
60
+ raise ModBus::Errors::IllegalFunction, "Illegal function: #{function_code}"
61
+ end
62
+
63
+ log "Server RX (#{msg.size} bytes): #{logging_bytes(msg)}"
64
+
65
+ msg
66
+ end
67
+
68
+ def serv_rtu_requests(io, &blk)
69
+ loop do
70
+ # read the RTU message
71
+ msg = read_rtu_request(io)
72
+ # If there is no RTU message, we're done serving this client
73
+ break if msg.nil?
74
+
75
+ if msg.getbyte(0) == @uid and msg[-2,2].unpack('n')[0] == crc16(msg[0..-3])
76
+ pdu = yield msg
77
+ resp = @uid.chr + pdu
78
+ resp << crc16(resp).to_word
79
+ log "Server TX (#{resp.size} bytes): #{logging_bytes(resp)}"
80
+ io.write resp
81
+ end
82
+ end
83
+ end
84
+
85
+ # Calc CRC16 for massage
17
86
  def crc16(msg)
18
87
  crc_lo = 0xff
19
88
  crc_hi = 0xff
@@ -65,5 +134,7 @@ module ModBus
65
134
  0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
66
135
  0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80,
67
136
  0x40]
137
+
68
138
  end
69
139
  end
140
+
@@ -1,6 +1,6 @@
1
1
  # RModBus - free implementation of ModBus protocol in Ruby.
2
2
  #
3
- # Copyright (C) 2009 Timin Aleksey
3
+ # Copyright (C) 2009-2011 Timin Aleksey
4
4
  #
5
5
  # This program is free software: you can redistribute it and/or modify
6
6
  # it under the terms of the GNU General Public License as published by
@@ -11,127 +11,31 @@
11
11
  # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
12
  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
13
  # GNU General Public License for more details.
14
- require 'rmodbus/crc16'
15
-
16
- begin
17
- require 'rubygems'
18
- rescue
19
- end
20
- require 'serialport'
21
-
22
14
  module ModBus
23
-
15
+ # RTU client implementation
16
+ # @example
17
+ # RTUClient.connect('/dev/ttyS1', 9600) do |cl|
18
+ # cl.with_slave(uid) do |slave|
19
+ # slave.holding_registers[0..100]
20
+ # end
21
+ # end
22
+ #
23
+ # @see RTUClient#open_connection
24
+ # @see Client#initialize
24
25
  class RTUClient < Client
25
-
26
- include CRC16
27
- attr_reader :port, :baud, :slave, :data_bits, :stop_bits, :parity, :read_timeout
28
- attr_accessor :debug
29
-
30
- # Connect with RTU server
31
- #
32
- # port - serial port of connections with RTU server
33
- #
34
- # baud - rate sp of connections with RTU server
35
- #
36
- # slaveaddr - slave ID of the RTU server
37
- #
38
- # Options:
39
- #
40
- # :data_bits => from 5 to 8
41
- #
42
- # :stop_bits => 1 or 2
43
- #
44
- # :parity => NONE, EVEN or ODD
45
- #
46
- # RTUClient.connect('/dev/port1') do |cl|
47
- #
48
- # put cl.read_holding_registers(0, 10)
49
- #
50
- # end
51
- def self.connect(port, baud=9600, slaveaddr=1, options = {})
52
- cl = RTUClient.new(port, baud, slaveaddr, options)
53
- yield cl
54
- cl.close
55
- end
56
-
57
- # Connect with RTU server
58
- #
59
- # port - serial port of connections with RTU server
60
- #
61
- # baud - rate sp of connections with RTU server
62
- #
63
- # data_bits - from 5 to 8
64
- #
65
- # stop_bits - 1 or 2
66
- #
67
- # parity - NONE, EVEN or ODD
68
- #
69
- # slaveaddr - slave ID of the RTU server # Connect with RTU server
70
- #
71
- # port - serial port of connections with RTU server
72
- #
73
- # baud - rate sp of connections with RTU server
74
- #
75
- # slaveaddr - slave ID of the RTU server
76
- #
77
- # Options:
78
- #
79
- # :data_bits => from 5 to 8
80
- #
81
- # :stop_bits => 1 or 2
82
- #
83
- # :parity => NONE, EVEN or ODD
84
- #
85
- # :read_timeout => default 5 ms
86
- def initialize(port, baud=9600, slaveaddr=1, options = {})
87
- @port, @baud, @slave = port, baud, slaveaddr
88
-
89
- @data_bits, @stop_bits, @parity, @read_timeout = 8, 1, SerialPort::NONE, 5
90
-
91
- @data_bits = options[:data_bits] unless options[:data_bits].nil?
92
- @stop_bits = options[:stop_bits] unless options[:stop_bits].nil?
93
- @parity = options[:parity] unless options[:parity].nil?
94
- @read_timeout = options[:read_timeout] unless options[:read_timeout].nil?
95
-
96
- @debug = false
97
-
98
- @sp = SerialPort.new(@port, @baud, @data_bits, @stop_bits, @parity)
99
- @sp.read_timeout = @read_timeout
100
-
101
- super()
102
- end
103
-
104
- def close
105
- @sp.close unless @sp.closed?
106
- end
107
-
108
- def closed?
109
- @sp.closed?
110
- end
26
+ include RTU
27
+ include SP
111
28
 
112
29
  protected
113
- def send_pdu(pdu)
114
- msg = @slave.chr + pdu
115
- msg << crc16(msg).to_word
116
- @sp.write msg
117
-
118
- log "Tx (#{msg.size} bytes): " + logging_bytes(msg)
30
+ # Open serial port
31
+ # @see SP#open_serial_port
32
+ def open_connection(port, baud=9600, opts = {})
33
+ open_serial_port(port, baud, opts)
119
34
  end
120
35
 
121
- def read_pdu
122
- msg = read_rtu_response(@sp)
123
-
124
- log "Rx (#{msg.size} bytes): " + logging_bytes(msg)
125
-
126
- if msg.getbyte(0) == @slave
127
- return msg[1..-3] if msg[-2,2].unpack('n')[0] == crc16(msg[0..-3])
128
- log "Ignore package: don't match CRC"
129
- else
130
- log "Ignore package: don't match slave ID"
131
- end
132
- loop do
133
- #waite timeout
134
- end
36
+ # @private
37
+ def get_slave(uid, io)
38
+ RTUSlave.new(uid, io)
135
39
  end
136
40
  end
137
41
  end