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
@@ -0,0 +1,268 @@
1
+ # RModBus - free implementation of ModBus protocol on Ruby.
2
+ #
3
+ # Copyright (C) 2008-2011 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
+ class Slave
18
+ include Errors
19
+ include Common
20
+ # Number of times to retry on read and read timeouts
21
+ attr_accessor :read_retries, :read_retry_timeout, :uid
22
+
23
+ Exceptions = {
24
+ 1 => IllegalFunction.new("The function code received in the query is not an allowable action for the server"),
25
+ 2 => IllegalDataAddress.new("The data address received in the query is not an allowable address for the server"),
26
+ 3 => IllegalDataValue.new("A value contained in the query data field is not an allowable value for server"),
27
+ 4 => SlaveDeviceFailure.new("An unrecoverable error occurred while the server was attempting to perform the requested action"),
28
+ 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"),
29
+ 6 => SlaveDeviceBus.new("The server is engaged in processing a long duration program command"),
30
+ 8 => MemoryParityError.new("The extended file area failed to pass a consistency check")
31
+ }
32
+ def initialize(uid, io)
33
+ @uid = uid
34
+ @read_retries = 10
35
+ @read_retry_timeout = 1
36
+ @io = io
37
+ end
38
+
39
+ # Returns a ModBus::ReadWriteProxy hash interface for coils
40
+ #
41
+ # @example
42
+ # coils[addr] => [1]
43
+ # coils[addr1..addr2] => [1, 0, ..]
44
+ # coils[addr] = 0 => [0]
45
+ # coils[addr1..addr2] = [1, 0, ..] => [1, 0, ..]
46
+ #
47
+ # @return [ReadWriteProxy] proxy object
48
+ def coils
49
+ ModBus::ReadWriteProxy.new(self, :coil)
50
+ end
51
+
52
+ # Read coils
53
+ #
54
+ # @example
55
+ # read_coils(addr, ncoils) => [1, 0, ..]
56
+ #
57
+ # @param [Integer] addr address first coil
58
+ # @param [Integer] ncoils number coils
59
+ # @return [Array] coils
60
+ def read_coils(addr, ncoils)
61
+ query("\x1" + addr.to_word + ncoils.to_word).unpack_bits[0..ncoils-1]
62
+ end
63
+ alias_method :read_coil, :read_coils
64
+
65
+ # Write a single coil
66
+ #
67
+ # @example
68
+ # write_single_coil(1, 0) => self
69
+ #
70
+ # @param [Integer] addr address coil
71
+ # @param [Integer] val value coil (0 or other)
72
+ # @return self
73
+ def write_single_coil(addr, val)
74
+ if val == 0
75
+ query("\x5" + addr.to_word + 0.to_word)
76
+ else
77
+ query("\x5" + addr.to_word + 0xff00.to_word)
78
+ end
79
+ self
80
+ end
81
+ alias_method :write_coil, :write_single_coil
82
+
83
+ # Write multiple coils
84
+ #
85
+ # @example
86
+ # write_multiple_coils(1, [0,1,0,1]) => self
87
+ #
88
+ # @param [Integer] addr address first coil
89
+ # @param [Array] vals written coils
90
+ def write_multiple_coils(addr, vals)
91
+ nbyte = ((vals.size-1) >> 3) + 1
92
+ sum = 0
93
+ (vals.size - 1).downto(0) do |i|
94
+ sum = sum << 1
95
+ sum |= 1 if vals[i] > 0
96
+ end
97
+
98
+ s_val = ""
99
+ nbyte.times do
100
+ s_val << (sum & 0xff).chr
101
+ sum >>= 8
102
+ end
103
+
104
+ query("\xf" + addr.to_word + vals.size.to_word + nbyte.chr + s_val)
105
+ self
106
+ end
107
+ alias_method :write_coils, :write_multiple_coils
108
+
109
+ # Returns a ModBus::ReadOnlyProxy hash interface for discrete inputs
110
+ #
111
+ # @example
112
+ # discrete_inputs[addr] => [1]
113
+ # discrete_inputs[addr1..addr2] => [1, 0, ..]
114
+ #
115
+ # @return [ReadOnlyProxy] proxy object
116
+ def discrete_inputs
117
+ ModBus::ReadOnlyProxy.new(self, :discrete_input)
118
+ end
119
+
120
+ # Read discrete inputs
121
+ #
122
+ # @example
123
+ # read_discrete_inputs(addr, ninputs) => [1, 0, ..]
124
+ #
125
+ # @param [Integer] addr address first input
126
+ # @param[Integer] ninputs number inputs
127
+ # @return [Array] inputs
128
+ def read_discrete_inputs(addr, ninputs)
129
+ query("\x2" + addr.to_word + ninputs.to_word).unpack_bits[0..ninputs-1]
130
+ end
131
+ alias_method :read_discrete_input, :read_discrete_inputs
132
+
133
+ # Returns a read/write ModBus::ReadOnlyProxy hash interface for coils
134
+ #
135
+ # @example
136
+ # input_registers[addr] => [1]
137
+ # input_registers[addr1..addr2] => [1, 0, ..]
138
+ #
139
+ # @return [ReadOnlyProxy] proxy object
140
+ def input_registers
141
+ ModBus::ReadOnlyProxy.new(self, :input_register)
142
+ end
143
+
144
+ # Read input registers
145
+ #
146
+ # @example
147
+ # read_input_registers(1, 5) => [1, 0, ..]
148
+ #
149
+ # @param [Integer] addr address first registers
150
+ # @param [Integer] nregs number registers
151
+ # @return [Array] registers
152
+ def read_input_registers(addr, nregs)
153
+ query("\x4" + addr.to_word + nregs.to_word).unpack('n*')
154
+ end
155
+ alias_method :read_input_register, :read_input_registers
156
+
157
+ # Returns a ModBus::ReadWriteProxy hash interface for holding registers
158
+ #
159
+ # @example
160
+ # holding_registers[addr] => [123]
161
+ # holding_registers[addr1..addr2] => [123, 234, ..]
162
+ # holding_registers[addr] = 123 => 123
163
+ # holding_registers[addr1..addr2] = [234, 345, ..] => [234, 345, ..]
164
+ #
165
+ # @return [ReadWriteProxy] proxy object
166
+ def holding_registers
167
+ ModBus::ReadWriteProxy.new(self, :holding_register)
168
+ end
169
+
170
+ # Read holding registers
171
+ #
172
+ # @example
173
+ # read_holding_registers(1, 5) => [1, 0, ..]
174
+ #
175
+ # @param [Integer] addr address first registers
176
+ # @param [Integer] nregs number registers
177
+ # @return [Array] registers
178
+ def read_holding_registers(addr, nregs)
179
+ query("\x3" + addr.to_word + nregs.to_word).unpack('n*')
180
+ end
181
+ alias_method :read_holding_register, :read_holding_registers
182
+
183
+ # Write a single holding register
184
+ #
185
+ # @example
186
+ # write_single_register(1, 0xaa) => self
187
+ #
188
+ # @param [Integer] addr address registers
189
+ # @param [Integer] val written to register
190
+ # @return self
191
+ def write_single_register(addr, val)
192
+ query("\x6" + addr.to_word + val.to_word)
193
+ self
194
+ end
195
+ alias_method :write_holding_register, :write_single_register
196
+
197
+
198
+ # Write multiple holding registers
199
+ #
200
+ # @example
201
+ # write_multiple_registers(1, [0xaa, 0]) => self
202
+ #
203
+ # @param [Integer] addr address first registers
204
+ # @param [Array] val written registers
205
+ # @return self
206
+ def write_multiple_registers(addr, vals)
207
+ s_val = ""
208
+ vals.each do |reg|
209
+ s_val << reg.to_word
210
+ end
211
+
212
+ query("\x10" + addr.to_word + vals.size.to_word + (vals.size * 2).chr + s_val)
213
+ self
214
+ end
215
+ alias_method :write_holding_registers, :write_multiple_registers
216
+
217
+ # Mask a holding register
218
+ #
219
+ # @example
220
+ # mask_write_register(1, 0xAAAA, 0x00FF) => self
221
+ # @param [Integer] addr address registers
222
+ # @param [Integer] and_mask mask for AND operation
223
+ # @param [Integer] or_mask mask for OR operation
224
+ def mask_write_register(addr, and_mask, or_mask)
225
+ query("\x16" + addr.to_word + and_mask.to_word + or_mask.to_word)
226
+ self
227
+ end
228
+
229
+ # Request pdu to slave device
230
+ #
231
+ # @param [String] pdu request to slave
232
+ # @return [String] received data
233
+ #
234
+ # @raise [ModBusTimeout] timed out during read attempt
235
+ # @raise [ModBusException] unknown error
236
+ # @raise [IllegalFunction] function code received in the query is not an allowable action for the server
237
+ # @raise [IllegalDataAddress] data address received in the query is not an allowable address for the server
238
+ # @raise [IllegalDataValue] value contained in the query data field is not an allowable value for server
239
+ # @raise [SlaveDeviceFailure] unrecoverable error occurred while the server was attempting to perform the requested action
240
+ # @raise [Acknowledge] server has accepted the request and is processing it, but a long duration of time will be required to do so
241
+ # @raise [SlaveDeviceBus] server is engaged in processing a long duration program command
242
+ # @raise [MemoryParityError] extended file area failed to pass a consistency check
243
+ def query(pdu)
244
+ tried = 0
245
+ begin
246
+ timeout(@read_retry_timeout, ModBusTimeout) do
247
+ send_pdu(pdu)
248
+ pdu = read_pdu
249
+ end
250
+ rescue ModBusTimeout => err
251
+ log "Timeout of read operation: (#{@read_retries - tried})"
252
+ tried += 1
253
+ retry unless tried >= @read_retries
254
+ raise ModBusTimeout.new, "Timed out during read attempt"
255
+ end
256
+
257
+ return nil if pdu.size == 0
258
+
259
+ if pdu.getbyte(0) >= 0x80
260
+ exc_id = pdu.getbyte(1)
261
+ raise Exceptions[exc_id] unless Exceptions[exc_id].nil?
262
+
263
+ raise ModBusException.new, "Unknown error"
264
+ end
265
+ pdu[2..-1]
266
+ end
267
+ end
268
+ end
data/lib/rmodbus/sp.rb ADDED
@@ -0,0 +1,45 @@
1
+ # RModBus - free implementation of ModBus protocol in Ruby.
2
+ #
3
+ # Copyright (C) 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
+ require 'serialport'
16
+
17
+ module ModBus
18
+ module SP
19
+ attr_reader :port, :baud, :data_bits, :stop_bits, :parity, :read_timeout
20
+ # Open serial port
21
+ # @param [String] port name serial ports ("/dev/ttyS0" POSIX, "com1" - Windows)
22
+ # @param [Integer] baud rate serial port (default 9600)
23
+ # @param [Hash] opts the options of serial port
24
+ #
25
+ # @option opts [Integer] :data_bits from 5 to 8
26
+ # @option opts [Integer] :stop_bits 1 or 2
27
+ # @option opts [Integer] :parity NONE, EVEN or ODD
28
+ # @option opts [Integer] :read_timeout default 100 ms
29
+ # @return [SerialPort] io serial port
30
+ def open_serial_port(port, baud, opts = {})
31
+ @port, @baud = port, baud
32
+
33
+ @data_bits, @stop_bits, @parity, @read_timeout = 8, 1, SerialPort::NONE, 100
34
+
35
+ @data_bits = opts[:data_bits] unless opts[:data_bits].nil?
36
+ @stop_bits = opts[:stop_bits] unless opts[:stop_bits].nil?
37
+ @parity = opts[:parity] unless opts[:parity].nil?
38
+ @read_timeout = options[:read_timeout] unless opts[:read_timeout].nil?
39
+
40
+ io = SerialPort.new(@port, @baud, @data_bits, @stop_bits, @parity)
41
+ io.read_timeout = @read_timeout
42
+ io
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,49 @@
1
+ # RModBus - free implementation of ModBus protocol on Ruby.
2
+ #
3
+ # Copyright (C) 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
+ require 'socket'
15
+ require 'timeout'
16
+
17
+ module ModBus
18
+ module TCP
19
+ include Timeout
20
+ attr_reader :ipaddr, :port
21
+
22
+ private
23
+ # Open TCP socket
24
+ #
25
+ # @param [String] ipaddr IP address of remote server
26
+ # @param [Integer] port connection port
27
+ # @param [Hash] opts options of connection
28
+ # @option opts [Float, Integer] :connect_timeout seconds timeout for open socket
29
+ # @return [TCPSocket] socket
30
+ #
31
+ # @raise [ModBusTimeout] timed out attempting to create connection
32
+ def open_tcp_connection(ipaddr, port, opts = {})
33
+ @ipaddr, @port = ipaddr, port
34
+
35
+ opts[:connect_timeout] ||= 1
36
+
37
+ io = nil
38
+ begin
39
+ timeout(opts[:connect_timeout], ModBusTimeout) do
40
+ io = TCPSocket.new(@ipaddr, @port)
41
+ end
42
+ rescue ModBusTimeout => err
43
+ raise ModBusTimeout.new, 'Timed out attempting to create connection'
44
+ end
45
+
46
+ io
47
+ end
48
+ end
49
+ end
@@ -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
@@ -11,98 +11,29 @@
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 'socket'
15
- require 'timeout'
16
- require 'rmodbus/client'
17
- require 'rmodbus/exceptions'
18
-
19
14
  module ModBus
20
-
21
- # Implementation clients(master) ModBusTCP
15
+ # TCP client implementation
16
+ # @example
17
+ # TCPClient.connect('127.0.0.1', 502) do |cl|
18
+ # cl.with_slave(uid) do |slave|
19
+ # slave.holding_registers[0..100]
20
+ # end
21
+ # end
22
+ #
23
+ # @see TCPClient#open_connection
24
+ # @see Client#initialize
22
25
  class TCPClient < Client
26
+ include TCP
23
27
 
24
- include Timeout
25
-
26
- attr_reader :ipaddr, :port, :slave, :transaction
27
- attr_accessor :debug
28
-
29
- # Connect with ModBus server
30
- #
31
- # ipaddr - ip of the server
32
- #
33
- # port - port TCP connections
34
- #
35
- # slaveaddr - slave ID of the server
36
- #
37
- # TCPClient.connect('127.0.0.1') do |cl|
38
- #
39
- # put cl.read_holding_registers(0, 10)
40
- #
41
- # end
42
- def self.connect(ipaddr, port = 502, slaveaddr = 1)
43
- cl = TCPClient.new(ipaddr, port, slaveaddr)
44
- yield cl
45
- cl.close
46
- end
47
-
48
- # Connect with a ModBus server
49
- #
50
- # ipaddr - ip of the server
51
- #
52
- # port - port TCP connections
53
- #
54
- # slaveaddr - slave ID of the server
55
- def initialize(ipaddr, port = 502, slaveaddr = 1)
56
- @transaction = 0
57
- @ipaddr, @port = ipaddr, port
58
- tried = 0
59
- begin
60
- timeout(1, ModBusTimeout) do
61
- @sock = TCPSocket.new(@ipaddr, @port)
62
- end
63
- rescue ModBusTimeout => err
64
- raise ModBusTimeout.new, 'Timed out attempting to create connection'
65
- end
66
- @slave = slaveaddr
67
- @debug = false
68
- super()
69
- end
70
-
71
- # Close TCP connections
72
- def close
73
- @sock.close unless @sock.closed?
28
+ protected
29
+ # Open TCP\IP connection
30
+ # @see TCP::open_connection
31
+ def open_connection(ipaddr, port = 502, opts = {})
32
+ open_tcp_connection(ipaddr, port, opts)
74
33
  end
75
34
 
76
- # Check TCP connections
77
- def closed?
78
- @sock.closed?
35
+ def get_slave(uid, io)
36
+ TCPSlave.new(uid, io)
79
37
  end
80
-
81
- private
82
- def send_pdu(pdu)
83
- @transaction = 0 if @transaction.next > 65535
84
- @transaction += 1
85
- msg = @transaction.to_word + "\0\0" + (pdu.size + 1).to_word + @slave.chr + pdu
86
- @sock.write msg
87
-
88
- log "Tx (#{msg.size} bytes): " + logging_bytes(msg)
89
- end
90
-
91
- def read_pdu
92
- header = @sock.read(7)
93
- if header
94
- tin = header[0,2].unpack('n')[0]
95
- raise Errors::ModBusException.new("Transaction number mismatch") unless tin == @transaction
96
- len = header[4,2].unpack('n')[0]
97
- msg = @sock.read(len-1)
98
-
99
- log "Rx (#{(header + msg).size} bytes): " + logging_bytes(header + msg)
100
- msg
101
- else
102
- raise Errors::ModBusException.new("Server did not respond")
103
- end
104
- end
105
-
106
38
  end
107
-
108
39
  end
@@ -11,35 +11,32 @@
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/parsers'
15
14
  require 'gserver'
16
15
 
17
16
  module ModBus
17
+ # TCP server implementation
18
+ # @example
19
+ # srv = TCPServer.new(10002, 1)
20
+ # srv.coils = [1,0,1,1]
21
+ # srv.discrete_inputs = [1,1,0,0]
22
+ # srv.holding_registers = [1,2,3,4]
23
+ # srv.input_registers = [1,2,3,4]
24
+ # srv.debug = true
25
+ # srv.start
18
26
  class TCPServer < GServer
19
- include Parsers
20
27
  include Common
21
-
22
- attr_accessor :coils, :discrete_inputs, :holding_registers, :input_registers, :debug
23
-
24
- def discret_inputs
25
- warn "[DEPRECATION] `discret_inputs` is deprecated. Please use `discrete_inputs` instead."
26
- @discrete_inputs
27
- end
28
-
29
- def discret_inputs=(val)
30
- warn "[DEPRECATION] `discret_inputs=` is deprecated. Please use `discrete_inputs=` instead."
31
- @discrete_inputs=val
32
- end
33
-
28
+ include Server
29
+
30
+ # Init server
31
+ # @param [Integer] port listen port
32
+ # @param [Integer] uid slave device
34
33
  def initialize(port = 502, uid = 1)
35
- @coils = []
36
- @discrete_inputs = []
37
- @holding_registers =[]
38
- @input_registers = []
39
34
  @uid = uid
40
35
  super(port)
41
36
  end
42
37
 
38
+ # Serve requests
39
+ # @param [TCPSocket] io socket
43
40
  def serve(io)
44
41
  loop do
45
42
  req = io.read(7)
@@ -0,0 +1,64 @@
1
+ # RModBus - free implementation of ModBus protocol on Ruby.
2
+ #
3
+ # Copyright (C) 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
+ module ModBus
15
+ # TCP slave implementation
16
+ # @example
17
+ # TCP.connect('127.0.0.1', 10002) do |cl|
18
+ # cl.with_slave(uid) do |slave|
19
+ # slave.holding_registers[0..100]
20
+ # end
21
+ # end
22
+ #
23
+ # @see RTUViaTCPClient#open_connection
24
+ # @see Client#with_slave
25
+ # @see Slave
26
+ class TCPSlave < Slave
27
+ attr_reader :transaction
28
+
29
+ # @see Slave::initialize
30
+ def initialize(uid, io)
31
+ @transaction = 0
32
+ super(uid, io)
33
+ end
34
+
35
+ private
36
+ # overide method for RTU over TCP implamentaion
37
+ # @see Slave#query
38
+ def send_pdu(pdu)
39
+ @transaction = 0 if @transaction.next > 65535
40
+ @transaction += 1
41
+ msg = @transaction.to_word + "\0\0" + (pdu.size + 1).to_word + @uid.chr + pdu
42
+ @io.write msg
43
+
44
+ log "Tx (#{msg.size} bytes): " + logging_bytes(msg)
45
+ end
46
+
47
+ # overide method for RTU over TCP implamentaion
48
+ # @see Slave#query
49
+ def read_pdu
50
+ header = @io.read(7)
51
+ if header
52
+ tin = header[0,2].unpack('n')[0]
53
+ raise Errors::ModBusException.new("Transaction number mismatch") unless tin == @transaction
54
+ len = header[4,2].unpack('n')[0]
55
+ msg = @io.read(len-1)
56
+
57
+ log "Rx (#{(header + msg).size} bytes): " + logging_bytes(header + msg)
58
+ msg
59
+ else
60
+ raise Errors::ModBusException.new("Server did not respond")
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,17 @@
1
+ # RModBus - free implementation of ModBus protocol on Ruby.
2
+ #
3
+ # Copyright (C) 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
+ module ModBus
15
+ # Package version
16
+ VERSION = '1.0.0'
17
+ end
data/lib/rmodbus.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # RModBus - free implementation of ModBus protocol on Ruby.
2
- # Copyright (C) 2008 Timin Aleksey
2
+ # Copyright (C) 2008 - 2011 Timin Aleksey
3
3
  # This program is free software: you can redistribute it and/or modify
4
4
  # it under the terms of the GNU General Public License as published by
5
5
  # the Free Software Foundation, either version 3 of the License, or
@@ -9,11 +9,27 @@
9
9
  # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
11
  # GNU General Public License for more details.
12
+ require 'rmodbus/errors'
13
+ require 'rmodbus/ext'
12
14
  require 'rmodbus/common'
15
+ require 'rmodbus/rtu'
16
+ require 'rmodbus/tcp'
17
+ require 'rmodbus/slave'
18
+ require 'rmodbus/client'
19
+ require 'rmodbus/server'
20
+ require 'rmodbus/tcp_slave'
13
21
  require 'rmodbus/tcp_client'
14
22
  require 'rmodbus/tcp_server'
15
- require 'rmodbus/rtu_client'
16
- require 'rmodbus/rtu_server'
23
+
24
+ # jruby not support serial RTU protocol yet
25
+ unless RUBY_PLATFORM == "java"
26
+ require 'rmodbus/sp'
27
+ require 'rmodbus/rtu_slave'
28
+ require 'rmodbus/rtu_client'
29
+ require 'rmodbus/rtu_server'
30
+ end
31
+
32
+ require 'rmodbus/rtu_via_tcp_slave'
17
33
  require 'rmodbus/rtu_via_tcp_client'
18
34
  require 'rmodbus/rtu_via_tcp_server'
19
-
35
+ require 'rmodbus/proxy'