rmodbus 1.3.2 → 2.1.2
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.
- checksums.yaml +5 -5
 - data/NEWS.md +19 -0
 - data/README.md +8 -8
 - data/examples/perfomance_rtu.rb +55 -56
 - data/examples/perfomance_rtu_via_tcp.rb +54 -55
 - data/examples/perfomance_tcp.rb +54 -55
 - data/examples/simple_xpca_gateway.rb +85 -0
 - data/examples/use_rtu_via_tcp_modbus.rb +14 -11
 - data/examples/use_tcp_modbus.rb +14 -11
 - data/lib/rmodbus/client/slave.rb +333 -0
 - data/lib/rmodbus/client.rb +15 -10
 - data/lib/rmodbus/debug.rb +12 -15
 - data/lib/rmodbus/errors.rb +26 -2
 - data/lib/rmodbus/ext.rb +72 -51
 - data/lib/rmodbus/options.rb +4 -1
 - data/lib/rmodbus/proxy.rb +14 -9
 - data/lib/rmodbus/rtu.rb +89 -125
 - data/lib/rmodbus/rtu_client.rb +22 -2
 - data/lib/rmodbus/rtu_server.rb +16 -12
 - data/lib/rmodbus/rtu_slave.rb +26 -3
 - data/lib/rmodbus/rtu_via_tcp_server.rb +12 -19
 - data/lib/rmodbus/server/slave.rb +18 -0
 - data/lib/rmodbus/server.rb +227 -84
 - data/lib/rmodbus/sp.rb +10 -12
 - data/lib/rmodbus/tcp.rb +9 -10
 - data/lib/rmodbus/tcp_client.rb +3 -0
 - data/lib/rmodbus/tcp_server.rb +41 -35
 - data/lib/rmodbus/tcp_slave.rb +19 -18
 - data/lib/rmodbus/version.rb +3 -2
 - data/lib/rmodbus.rb +20 -21
 - metadata +63 -50
 - data/Rakefile +0 -29
 - data/examples/simple-xpca-gateway.rb +0 -84
 - data/lib/rmodbus/rtu_via_tcp_client.rb +0 -26
 - data/lib/rmodbus/rtu_via_tcp_slave.rb +0 -29
 - data/lib/rmodbus/slave.rb +0 -308
 - data/spec/client_spec.rb +0 -88
 - data/spec/exception_spec.rb +0 -119
 - data/spec/ext_spec.rb +0 -52
 - data/spec/logging_spec.rb +0 -89
 - data/spec/proxy_spec.rb +0 -74
 - data/spec/read_rtu_response_spec.rb +0 -92
 - data/spec/response_mismach_spec.rb +0 -163
 - data/spec/rtu_client_spec.rb +0 -86
 - data/spec/rtu_server_spec.rb +0 -30
 - data/spec/rtu_via_tcp_client_spec.rb +0 -76
 - data/spec/rtu_via_tcp_server_spec.rb +0 -16
 - data/spec/slave_spec.rb +0 -55
 - data/spec/spec_helper.rb +0 -54
 - data/spec/tcp_client_spec.rb +0 -88
 - data/spec/tcp_server_spec.rb +0 -129
 
    
        data/lib/rmodbus/proxy.rb
    CHANGED
    
    | 
         @@ -1,3 +1,5 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            module ModBus
         
     | 
| 
       2 
4 
     | 
    
         
             
              # Given a slave and a type of operation, execute a single or multiple read using hash syntax
         
     | 
| 
       3 
5 
     | 
    
         
             
              class ReadOnlyProxy
         
     | 
| 
         @@ -10,9 +12,9 @@ module ModBus 
     | 
|
| 
       10 
12 
     | 
    
         
             
                # Note that in the case of multiples, a pluralized version of the method is sent to the slave
         
     | 
| 
       11 
13 
     | 
    
         
             
                def [](key)
         
     | 
| 
       12 
14 
     | 
    
         
             
                  if key.instance_of?(0.class)
         
     | 
| 
       13 
     | 
    
         
            -
                    @slave.send("read_#{@type}", key 
     | 
| 
      
 15 
     | 
    
         
            +
                    @slave.send(:"read_#{@type}", key)
         
     | 
| 
       14 
16 
     | 
    
         
             
                  elsif key.instance_of?(Range)
         
     | 
| 
       15 
     | 
    
         
            -
                    @slave.send("read_#{@type}s", key.first, key.count)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    @slave.send(:"read_#{@type}s", key.first, key.count)
         
     | 
| 
       16 
18 
     | 
    
         
             
                  else
         
     | 
| 
       17 
19 
     | 
    
         
             
                    raise ModBus::Errors::ProxyException, "Invalid argument, must be integer or range. Was #{key.class}"
         
     | 
| 
       18 
20 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -20,22 +22,25 @@ module ModBus 
     | 
|
| 
       20 
22 
     | 
    
         
             
              end
         
     | 
| 
       21 
23 
     | 
    
         | 
| 
       22 
24 
     | 
    
         
             
              class ReadWriteProxy < ReadOnlyProxy
         
     | 
| 
       23 
     | 
    
         
            -
                # Write single or multiple values to a modbus slave depending on whether a 
     | 
| 
       24 
     | 
    
         
            -
                #  
     | 
| 
       25 
     | 
    
         
            -
                #  
     | 
| 
      
 25 
     | 
    
         
            +
                # Write single or multiple values to a modbus slave depending on whether a
         
     | 
| 
      
 26 
     | 
    
         
            +
                # Fixnum or a Range was given.
         
     | 
| 
      
 27 
     | 
    
         
            +
                # Note that in the case of multiples, a pluralized version of the method is
         
     | 
| 
      
 28 
     | 
    
         
            +
                # sent to the slave. Also when writing multiple values, the number of
         
     | 
| 
      
 29 
     | 
    
         
            +
                # elements must match the number of registers in the range or an exception
         
     | 
| 
      
 30 
     | 
    
         
            +
                # is raised
         
     | 
| 
       26 
31 
     | 
    
         
             
                def []=(key, val)
         
     | 
| 
       27 
32 
     | 
    
         
             
                  if key.instance_of?(0.class)
         
     | 
| 
       28 
     | 
    
         
            -
                    @slave.send("write_#{@type}", key, val)
         
     | 
| 
      
 33 
     | 
    
         
            +
                    @slave.send(:"write_#{@type}", key, val)
         
     | 
| 
       29 
34 
     | 
    
         
             
                  elsif key.instance_of?(Range)
         
     | 
| 
       30 
35 
     | 
    
         
             
                    if key.count != val.size
         
     | 
| 
       31 
     | 
    
         
            -
                      raise ModBus::Errors::ProxyException, 
     | 
| 
      
 36 
     | 
    
         
            +
                      raise ModBus::Errors::ProxyException,
         
     | 
| 
      
 37 
     | 
    
         
            +
                            "The size of the range must match the size of the values (#{key.count} != #{val.size})"
         
     | 
| 
       32 
38 
     | 
    
         
             
                    end
         
     | 
| 
       33 
39 
     | 
    
         | 
| 
       34 
     | 
    
         
            -
                    @slave.send("write_#{@type}s", key.first, val)
         
     | 
| 
      
 40 
     | 
    
         
            +
                    @slave.send(:"write_#{@type}s", key.first, val)
         
     | 
| 
       35 
41 
     | 
    
         
             
                  else
         
     | 
| 
       36 
42 
     | 
    
         
             
                    raise ModBus::Errors::ProxyException, "Invalid argument, must be integer or range. Was #{key.class}"
         
     | 
| 
       37 
43 
     | 
    
         
             
                  end
         
     | 
| 
       38 
44 
     | 
    
         
             
                end
         
     | 
| 
       39 
45 
     | 
    
         
             
              end
         
     | 
| 
       40 
     | 
    
         
            -
             
     | 
| 
       41 
46 
     | 
    
         
             
            end
         
     | 
    
        data/lib/rmodbus/rtu.rb
    CHANGED
    
    | 
         @@ -1,31 +1,33 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "digest/crc16_modbus"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "io/wait"
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
       1 
6 
     | 
    
         
             
            module ModBus
         
     | 
| 
       2 
7 
     | 
    
         
             
              module RTU
         
     | 
| 
       3 
8 
     | 
    
         
             
                private
         
     | 
| 
       4 
9 
     | 
    
         | 
| 
       5 
10 
     | 
    
         
             
                # We have to read specific amounts of numbers of bytes from the network depending on the function code and content
         
     | 
| 
       6 
11 
     | 
    
         
             
                def read_rtu_response(io)
         
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
                  msg =  
     | 
| 
       9 
     | 
    
         
            -
                  while msg.nil?
         
     | 
| 
       10 
     | 
    
         
            -
            	      msg = io.read(2)
         
     | 
| 
       11 
     | 
    
         
            -
                  end
         
     | 
| 
      
 12 
     | 
    
         
            +
                  # Read the slave_id and function code
         
     | 
| 
      
 13 
     | 
    
         
            +
                  msg = read(io, 2)
         
     | 
| 
       12 
14 
     | 
    
         | 
| 
       13 
15 
     | 
    
         
             
                  function_code = msg.getbyte(1)
         
     | 
| 
       14 
16 
     | 
    
         
             
                  case function_code
         
     | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
      
 17 
     | 
    
         
            +
                  when 1, 2, 3, 4
         
     | 
| 
      
 18 
     | 
    
         
            +
                    # read the third byte to find out how much more
         
     | 
| 
      
 19 
     | 
    
         
            +
                    # we need to read + CRC
         
     | 
| 
      
 20 
     | 
    
         
            +
                    msg += read(io, 1)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    msg + read(io, msg.getbyte(2) + 2)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  when 5, 6, 15, 16
         
     | 
| 
      
 23 
     | 
    
         
            +
                    # We just read in an additional 6 bytes
         
     | 
| 
      
 24 
     | 
    
         
            +
                    msg + read(io, 6)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  when 22
         
     | 
| 
      
 26 
     | 
    
         
            +
                    msg + read(io, 8)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  when 0x80..0xff
         
     | 
| 
      
 28 
     | 
    
         
            +
                    msg + read(io, 3)
         
     | 
| 
      
 29 
     | 
    
         
            +
                  else
         
     | 
| 
      
 30 
     | 
    
         
            +
                    raise ModBus::Errors::IllegalFunction, "Illegal function: #{function_code}"
         
     | 
| 
       29 
31 
     | 
    
         
             
                  end
         
     | 
| 
       30 
32 
     | 
    
         
             
                end
         
     | 
| 
       31 
33 
     | 
    
         | 
| 
         @@ -38,127 +40,89 @@ module ModBus 
     | 
|
| 
       38 
40 
     | 
    
         
             
                  end
         
     | 
| 
       39 
41 
     | 
    
         
             
                end
         
     | 
| 
       40 
42 
     | 
    
         | 
| 
       41 
     | 
    
         
            -
                def  
     | 
| 
       42 
     | 
    
         
            -
                   
     | 
| 
       43 
     | 
    
         
            -
                   
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
       46 
     | 
    
         
            -
             
     | 
| 
      
 43 
     | 
    
         
            +
                def read(io, len)
         
     | 
| 
      
 44 
     | 
    
         
            +
                  result = +""
         
     | 
| 
      
 45 
     | 
    
         
            +
                  loop do
         
     | 
| 
      
 46 
     | 
    
         
            +
                    this_iter = io.read(len - result.length)
         
     | 
| 
      
 47 
     | 
    
         
            +
                    result.concat(this_iter) if this_iter
         
     | 
| 
      
 48 
     | 
    
         
            +
                    return result if result.length == len
         
     | 
| 
       47 
49 
     | 
    
         | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
      
 50 
     | 
    
         
            +
                    io.wait_readable
         
     | 
| 
      
 51 
     | 
    
         
            +
                  end
         
     | 
| 
       49 
52 
     | 
    
         
             
                end
         
     | 
| 
       50 
53 
     | 
    
         | 
| 
       51 
     | 
    
         
            -
                def  
     | 
| 
       52 
     | 
    
         
            -
                   
     | 
| 
      
 54 
     | 
    
         
            +
                def read_rtu_request(io)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  # Every message is a minimum of 4 bytes (slave id, function code, crc16)
         
     | 
| 
      
 56 
     | 
    
         
            +
                  msg = read(io, 4)
         
     | 
| 
       53 
57 
     | 
    
         | 
| 
       54 
     | 
    
         
            -
                   
     | 
| 
      
 58 
     | 
    
         
            +
                  # If msg is nil, then our client never sent us anything and it's time to disconnect
         
     | 
| 
      
 59 
     | 
    
         
            +
                  return if msg.nil?
         
     | 
| 
       55 
60 
     | 
    
         | 
| 
       56 
     | 
    
         
            -
                  if msg.getbyte(0) == @uid
         
     | 
| 
       57 
     | 
    
         
            -
                    return msg[1..-3] if msg[-2,2].unpack('n')[0] == crc16(msg[0..-3])
         
     | 
| 
       58 
     | 
    
         
            -
                    log "Ignore package: don't match CRC"
         
     | 
| 
       59 
     | 
    
         
            -
                  else
         
     | 
| 
       60 
     | 
    
         
            -
                    log "Ignore package: don't match uid ID"
         
     | 
| 
       61 
     | 
    
         
            -
                  end
         
     | 
| 
       62 
61 
     | 
    
         
             
                  loop do
         
     | 
| 
       63 
     | 
    
         
            -
                     
     | 
| 
       64 
     | 
    
         
            -
                     
     | 
| 
      
 62 
     | 
    
         
            +
                    offset = 0
         
     | 
| 
      
 63 
     | 
    
         
            +
                    crc = msg[-2..].unpack1("S<")
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                    # scan the bytestream for a valid CRC
         
     | 
| 
      
 66 
     | 
    
         
            +
                    loop do
         
     | 
| 
      
 67 
     | 
    
         
            +
                      break if offset >= msg.length - 3
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                      calculated_crc = Digest::CRC16Modbus.checksum(msg[offset..-3])
         
     | 
| 
      
 70 
     | 
    
         
            +
                      if crc == calculated_crc
         
     | 
| 
      
 71 
     | 
    
         
            +
                        is_response = (msg.getbyte(offset + 1) & 0x80 == 0x80) ||
         
     | 
| 
      
 72 
     | 
    
         
            +
                                      (msg.getbyte(offset) == @last_req_uid &&
         
     | 
| 
      
 73 
     | 
    
         
            +
                                          msg.getbyte(offset + 1) == @last_req_func &&
         
     | 
| 
      
 74 
     | 
    
         
            +
                                      @last_req_timestamp && Time.now.to_f - @last_req_timestamp < 5)
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                        params = if is_response
         
     | 
| 
      
 77 
     | 
    
         
            +
                                   parse_response(msg.getbyte(offset + 1), msg[(offset + 1)..-3])
         
     | 
| 
      
 78 
     | 
    
         
            +
                                 else
         
     | 
| 
      
 79 
     | 
    
         
            +
                                   parse_request(msg.getbyte(offset + 1), msg[(offset + 1)..-3])
         
     | 
| 
      
 80 
     | 
    
         
            +
                                 end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                        unless params.nil?
         
     | 
| 
      
 83 
     | 
    
         
            +
                          if is_response
         
     | 
| 
      
 84 
     | 
    
         
            +
                            @last_req_uid = @last_req_func = @last_req_timestamp = nil
         
     | 
| 
      
 85 
     | 
    
         
            +
                          else
         
     | 
| 
      
 86 
     | 
    
         
            +
                            @last_req_uid = msg.getbyte(offset)
         
     | 
| 
      
 87 
     | 
    
         
            +
                            @last_req_func = msg.getbyte(offset + 1)
         
     | 
| 
      
 88 
     | 
    
         
            +
                            @last_req_timestamp = Time.now.to_f
         
     | 
| 
      
 89 
     | 
    
         
            +
                          end
         
     | 
| 
      
 90 
     | 
    
         
            +
                          log "Server RX discarding #{offset} bytes: #{logging_bytes(msg[0...offset])}" if offset != 0
         
     | 
| 
      
 91 
     | 
    
         
            +
                          log "Server RX (#{msg.size - offset} bytes): #{logging_bytes(msg[offset..])}"
         
     | 
| 
      
 92 
     | 
    
         
            +
                          return [msg.getbyte(offset), msg.getbyte(offset + 1), params, msg[offset + 1..-3], is_response]
         
     | 
| 
      
 93 
     | 
    
         
            +
                        end
         
     | 
| 
      
 94 
     | 
    
         
            +
                      end
         
     | 
| 
      
 95 
     | 
    
         
            +
                      offset += 1
         
     | 
| 
      
 96 
     | 
    
         
            +
                    end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                    msg.concat(read(io, 1))
         
     | 
| 
      
 99 
     | 
    
         
            +
                    # maximum message size is 256, so that's as far as we have to
         
     | 
| 
      
 100 
     | 
    
         
            +
                    # be able to see at once
         
     | 
| 
      
 101 
     | 
    
         
            +
                    msg = msg[1..] if msg.length > 256
         
     | 
| 
       65 
102 
     | 
    
         
             
                  end
         
     | 
| 
       66 
103 
     | 
    
         
             
                end
         
     | 
| 
       67 
104 
     | 
    
         | 
| 
       68 
     | 
    
         
            -
                def  
     | 
| 
       69 
     | 
    
         
            -
            			# Read the slave_id and function code
         
     | 
| 
       70 
     | 
    
         
            -
            			msg = io.read(2)
         
     | 
| 
       71 
     | 
    
         
            -
             
     | 
| 
       72 
     | 
    
         
            -
            			# If msg is nil, then our client never sent us anything and it's time to disconnect
         
     | 
| 
       73 
     | 
    
         
            -
            			return if msg.nil?
         
     | 
| 
       74 
     | 
    
         
            -
             
     | 
| 
       75 
     | 
    
         
            -
            			function_code = msg.getbyte(1)
         
     | 
| 
       76 
     | 
    
         
            -
            			if [1, 2, 3, 4, 5, 6].include?(function_code)
         
     | 
| 
       77 
     | 
    
         
            -
            				# read 6 more bytes and return the message total message
         
     | 
| 
       78 
     | 
    
         
            -
            				msg += io.read(6)
         
     | 
| 
       79 
     | 
    
         
            -
            			elsif [15, 16].include?(function_code)
         
     | 
| 
       80 
     | 
    
         
            -
            				# Read in first register, register count, and data bytes
         
     | 
| 
       81 
     | 
    
         
            -
            				msg += io.read(5)
         
     | 
| 
       82 
     | 
    
         
            -
            				# Read in however much data we need to + 2 CRC bytes
         
     | 
| 
       83 
     | 
    
         
            -
            				msg += io.read(msg.getbyte(6) + 2)
         
     | 
| 
       84 
     | 
    
         
            -
            			else
         
     | 
| 
       85 
     | 
    
         
            -
            				raise ModBus::Errors::IllegalFunction, "Illegal function: #{function_code}"
         
     | 
| 
       86 
     | 
    
         
            -
            			end
         
     | 
| 
       87 
     | 
    
         
            -
             
     | 
| 
       88 
     | 
    
         
            -
            			log "Server RX (#{msg.size} bytes): #{logging_bytes(msg)}"
         
     | 
| 
       89 
     | 
    
         
            -
             
     | 
| 
       90 
     | 
    
         
            -
            			msg
         
     | 
| 
       91 
     | 
    
         
            -
            		end
         
     | 
| 
       92 
     | 
    
         
            -
             
     | 
| 
       93 
     | 
    
         
            -
                def serv_rtu_requests(io, &blk)
         
     | 
| 
      
 105 
     | 
    
         
            +
                def serve(io)
         
     | 
| 
       94 
106 
     | 
    
         
             
                  loop do
         
     | 
| 
       95 
107 
     | 
    
         
             
                    # read the RTU message
         
     | 
| 
       96 
     | 
    
         
            -
                     
     | 
| 
       97 
     | 
    
         
            -
             
     | 
| 
       98 
     | 
    
         
            -
                    next if msg.nil?
         
     | 
| 
       99 
     | 
    
         
            -
             
     | 
| 
       100 
     | 
    
         
            -
                    if msg.getbyte(0) == @uid and msg[-2,2].unpack('n')[0] == crc16(msg[0..-3])
         
     | 
| 
       101 
     | 
    
         
            -
                      pdu = yield msg
         
     | 
| 
       102 
     | 
    
         
            -
                      resp = @uid.chr + pdu
         
     | 
| 
       103 
     | 
    
         
            -
                      resp << crc16(resp).to_word
         
     | 
| 
       104 
     | 
    
         
            -
                      log "Server TX (#{resp.size} bytes): #{logging_bytes(resp)}"
         
     | 
| 
       105 
     | 
    
         
            -
                      io.write resp
         
     | 
| 
       106 
     | 
    
         
            -
            		    end
         
     | 
| 
       107 
     | 
    
         
            -
            	    end
         
     | 
| 
       108 
     | 
    
         
            -
                end
         
     | 
| 
      
 108 
     | 
    
         
            +
                    uid, func, params, pdu, is_response = read_rtu_request(io)
         
     | 
| 
       109 
109 
     | 
    
         | 
| 
       110 
     | 
    
         
            -
             
     | 
| 
       111 
     | 
    
         
            -
                def crc16(msg)
         
     | 
| 
       112 
     | 
    
         
            -
                  crc_lo = 0xff
         
     | 
| 
       113 
     | 
    
         
            -
                  crc_hi = 0xff
         
     | 
| 
      
 110 
     | 
    
         
            +
                    next if uid.nil?
         
     | 
| 
       114 
111 
     | 
    
         | 
| 
       115 
     | 
    
         
            -
             
     | 
| 
       116 
     | 
    
         
            -
                     
     | 
| 
       117 
     | 
    
         
            -
                    crc_hi = crc_lo ^ CrcHiTable[i]
         
     | 
| 
       118 
     | 
    
         
            -
                    crc_lo = CrcLoTable[i]
         
     | 
| 
       119 
     | 
    
         
            -
                  end
         
     | 
| 
      
 112 
     | 
    
         
            +
                    pdu = exec_req(uid, func, params, pdu, is_response: is_response)
         
     | 
| 
      
 113 
     | 
    
         
            +
                    next unless pdu
         
     | 
| 
       120 
114 
     | 
    
         | 
| 
       121 
     | 
    
         
            -
             
     | 
| 
      
 115 
     | 
    
         
            +
                    @last_req_uid = @last_req_func = @last_req_timestamp = nil
         
     | 
| 
      
 116 
     | 
    
         
            +
                    resp = uid.chr + pdu
         
     | 
| 
      
 117 
     | 
    
         
            +
                    resp << [crc16(resp)].pack("S<")
         
     | 
| 
      
 118 
     | 
    
         
            +
                    log "Server TX (#{resp.size} bytes): #{logging_bytes(resp)}"
         
     | 
| 
      
 119 
     | 
    
         
            +
                    io.write resp
         
     | 
| 
      
 120 
     | 
    
         
            +
                  end
         
     | 
| 
       122 
121 
     | 
    
         
             
                end
         
     | 
| 
       123 
122 
     | 
    
         | 
| 
       124 
     | 
    
         
            -
                 
     | 
| 
       125 
     | 
    
         
            -
             
     | 
| 
       126 
     | 
    
         
            -
             
     | 
| 
       127 
     | 
    
         
            -
             
     | 
| 
       128 
     | 
    
         
            -
                    0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
         
     | 
| 
       129 
     | 
    
         
            -
                    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
         
     | 
| 
       130 
     | 
    
         
            -
                    0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
         
     | 
| 
       131 
     | 
    
         
            -
                    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
         
     | 
| 
       132 
     | 
    
         
            -
                    0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
         
     | 
| 
       133 
     | 
    
         
            -
                    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
         
     | 
| 
       134 
     | 
    
         
            -
                    0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
         
     | 
| 
       135 
     | 
    
         
            -
                    0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
         
     | 
| 
       136 
     | 
    
         
            -
                    0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
         
     | 
| 
       137 
     | 
    
         
            -
                    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
         
     | 
| 
       138 
     | 
    
         
            -
                    0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
         
     | 
| 
       139 
     | 
    
         
            -
                    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
         
     | 
| 
       140 
     | 
    
         
            -
                    0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
         
     | 
| 
       141 
     | 
    
         
            -
                    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
         
     | 
| 
       142 
     | 
    
         
            -
                    0x40]
         
     | 
| 
       143 
     | 
    
         
            -
                CrcLoTable = [
         
     | 
| 
       144 
     | 
    
         
            -
                    0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4,
         
     | 
| 
       145 
     | 
    
         
            -
                    0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
         
     | 
| 
       146 
     | 
    
         
            -
                    0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD,
         
     | 
| 
       147 
     | 
    
         
            -
                    0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
         
     | 
| 
       148 
     | 
    
         
            -
                    0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7,
         
     | 
| 
       149 
     | 
    
         
            -
                    0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
         
     | 
| 
       150 
     | 
    
         
            -
                    0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE,
         
     | 
| 
       151 
     | 
    
         
            -
                    0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
         
     | 
| 
       152 
     | 
    
         
            -
                    0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2,
         
     | 
| 
       153 
     | 
    
         
            -
                    0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
         
     | 
| 
       154 
     | 
    
         
            -
                    0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB,
         
     | 
| 
       155 
     | 
    
         
            -
                    0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
         
     | 
| 
       156 
     | 
    
         
            -
                    0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91,
         
     | 
| 
       157 
     | 
    
         
            -
                    0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
         
     | 
| 
       158 
     | 
    
         
            -
                    0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88,
         
     | 
| 
       159 
     | 
    
         
            -
                    0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
         
     | 
| 
       160 
     | 
    
         
            -
                    0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80,
         
     | 
| 
       161 
     | 
    
         
            -
                    0x40]
         
     | 
| 
       162 
     | 
    
         
            -
             
     | 
| 
      
 123 
     | 
    
         
            +
                # Calc CRC16 for massage
         
     | 
| 
      
 124 
     | 
    
         
            +
                def crc16(msg)
         
     | 
| 
      
 125 
     | 
    
         
            +
                  Digest::CRC16Modbus.checksum(msg)
         
     | 
| 
      
 126 
     | 
    
         
            +
                end
         
     | 
| 
       163 
127 
     | 
    
         
             
              end
         
     | 
| 
       164 
128 
     | 
    
         
             
            end
         
     | 
    
        data/lib/rmodbus/rtu_client.rb
    CHANGED
    
    | 
         @@ -1,3 +1,5 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            module ModBus
         
     | 
| 
       2 
4 
     | 
    
         
             
              # RTU client implementation
         
     | 
| 
       3 
5 
     | 
    
         
             
              # @example
         
     | 
| 
         @@ -7,16 +9,34 @@ module ModBus 
     | 
|
| 
       7 
9 
     | 
    
         
             
              #     end
         
     | 
| 
       8 
10 
     | 
    
         
             
              #   end
         
     | 
| 
       9 
11 
     | 
    
         
             
              #
         
     | 
| 
      
 12 
     | 
    
         
            +
              # @example
         
     | 
| 
      
 13 
     | 
    
         
            +
              #   RTUClient.connect('127.0.0.1', 10002) do |cl|
         
     | 
| 
      
 14 
     | 
    
         
            +
              #     cl.with_slave(uid) do |slave|
         
     | 
| 
      
 15 
     | 
    
         
            +
              #       slave.holding_registers[0..100]
         
     | 
| 
      
 16 
     | 
    
         
            +
              #     end
         
     | 
| 
      
 17 
     | 
    
         
            +
              #   end
         
     | 
| 
      
 18 
     | 
    
         
            +
              #
         
     | 
| 
      
 19 
     | 
    
         
            +
              # @see TCP#open_tcp_connection
         
     | 
| 
       10 
20 
     | 
    
         
             
              # @see SP#open_serial_port
         
     | 
| 
       11 
21 
     | 
    
         
             
              # @see Client#initialize
         
     | 
| 
       12 
22 
     | 
    
         
             
              class RTUClient < Client
         
     | 
| 
       13 
23 
     | 
    
         
             
                include RTU
         
     | 
| 
       14 
24 
     | 
    
         
             
                include SP
         
     | 
| 
      
 25 
     | 
    
         
            +
                include TCP
         
     | 
| 
       15 
26 
     | 
    
         | 
| 
       16 
27 
     | 
    
         
             
                protected
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
       17 
29 
     | 
    
         
             
                # Open serial port
         
     | 
| 
       18 
     | 
    
         
            -
                def open_connection( 
     | 
| 
       19 
     | 
    
         
            -
                   
     | 
| 
      
 30 
     | 
    
         
            +
                def open_connection(port_or_ipaddr, arg = nil, opts = {})
         
     | 
| 
      
 31 
     | 
    
         
            +
                  if port_or_ipaddr.is_a?(IO) || port_or_ipaddr.respond_to?(:read)
         
     | 
| 
      
 32 
     | 
    
         
            +
                    port_or_ipaddr
         
     | 
| 
      
 33 
     | 
    
         
            +
                  elsif File.exist?(port_or_ipaddr) || port_or_ipaddr.start_with?("/dev") || port_or_ipaddr.start_with?("COM")
         
     | 
| 
      
 34 
     | 
    
         
            +
                    arg ||= 9600
         
     | 
| 
      
 35 
     | 
    
         
            +
                    open_serial_port(port_or_ipaddr, arg, opts)
         
     | 
| 
      
 36 
     | 
    
         
            +
                  else
         
     | 
| 
      
 37 
     | 
    
         
            +
                    arg ||= 10_002
         
     | 
| 
      
 38 
     | 
    
         
            +
                    open_tcp_connection(port_or_ipaddr, arg, opts)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end
         
     | 
| 
       20 
40 
     | 
    
         
             
                end
         
     | 
| 
       21 
41 
     | 
    
         | 
| 
       22 
42 
     | 
    
         
             
                def get_slave(uid, io)
         
     | 
    
        data/lib/rmodbus/rtu_server.rb
    CHANGED
    
    | 
         @@ -1,12 +1,15 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            module ModBus
         
     | 
| 
       2 
4 
     | 
    
         
             
              # RTU server implementation
         
     | 
| 
       3 
5 
     | 
    
         
             
              # @example
         
     | 
| 
       4 
     | 
    
         
            -
              #   srv = RTUServer.new('/dev/ttyS1', 9600 
     | 
| 
       5 
     | 
    
         
            -
              #    
     | 
| 
       6 
     | 
    
         
            -
              #    
     | 
| 
       7 
     | 
    
         
            -
              #    
     | 
| 
       8 
     | 
    
         
            -
              #    
     | 
| 
       9 
     | 
    
         
            -
              #    
     | 
| 
      
 6 
     | 
    
         
            +
              #   srv = RTUServer.new('/dev/ttyS1', 9600)
         
     | 
| 
      
 7 
     | 
    
         
            +
              #   slave = srv.with_slave(1)
         
     | 
| 
      
 8 
     | 
    
         
            +
              #   slave.coils = [1,0,1,1]
         
     | 
| 
      
 9 
     | 
    
         
            +
              #   slave.discrete_inputs = [1,1,0,0]
         
     | 
| 
      
 10 
     | 
    
         
            +
              #   slave.holding_registers = [1,2,3,4]
         
     | 
| 
      
 11 
     | 
    
         
            +
              #   slave.input_registers = [1,2,3,4]
         
     | 
| 
      
 12 
     | 
    
         
            +
              #   srv.logger = Logger.new($stdout)
         
     | 
| 
       10 
13 
     | 
    
         
             
              #   srv.start
         
     | 
| 
       11 
14 
     | 
    
         
             
              class RTUServer
         
     | 
| 
       12 
15 
     | 
    
         
             
                include Debug
         
     | 
| 
         @@ -17,18 +20,19 @@ module ModBus 
     | 
|
| 
       17 
20 
     | 
    
         
             
                # Init RTU server
         
     | 
| 
       18 
21 
     | 
    
         
             
                # @param [Integer] uid slave device
         
     | 
| 
       19 
22 
     | 
    
         
             
                # @see SP#open_serial_port
         
     | 
| 
       20 
     | 
    
         
            -
                def initialize(port, baud=9600,  
     | 
| 
      
 23 
     | 
    
         
            +
                def initialize(port, baud = 9600, opts = {})
         
     | 
| 
       21 
24 
     | 
    
         
             
                  Thread.abort_on_exception = true
         
     | 
| 
       22 
     | 
    
         
            -
                  @sp =  
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
      
 25 
     | 
    
         
            +
                  @sp = if port.is_a?(IO) || port.respond_to?(:read)
         
     | 
| 
      
 26 
     | 
    
         
            +
                          port
         
     | 
| 
      
 27 
     | 
    
         
            +
                        else
         
     | 
| 
      
 28 
     | 
    
         
            +
                          open_serial_port(port, baud, opts)
         
     | 
| 
      
 29 
     | 
    
         
            +
                        end
         
     | 
| 
       24 
30 
     | 
    
         
             
                end
         
     | 
| 
       25 
31 
     | 
    
         | 
| 
       26 
32 
     | 
    
         
             
                # Start server
         
     | 
| 
       27 
33 
     | 
    
         
             
                def start
         
     | 
| 
       28 
34 
     | 
    
         
             
                  @serv = Thread.new do
         
     | 
| 
       29 
     | 
    
         
            -
                     
     | 
| 
       30 
     | 
    
         
            -
                      exec_req(msg[1..-3], @coils, @discrete_inputs, @holding_registers, @input_registers)
         
     | 
| 
       31 
     | 
    
         
            -
                    end
         
     | 
| 
      
 35 
     | 
    
         
            +
                    serve(@sp)
         
     | 
| 
       32 
36 
     | 
    
         
             
                  end
         
     | 
| 
       33 
37 
     | 
    
         
             
                end
         
     | 
| 
       34 
38 
     | 
    
         | 
    
        data/lib/rmodbus/rtu_slave.rb
    CHANGED
    
    | 
         @@ -1,3 +1,5 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            module ModBus
         
     | 
| 
       2 
4 
     | 
    
         
             
              # RTU slave implementation
         
     | 
| 
       3 
5 
     | 
    
         
             
              # @example
         
     | 
| 
         @@ -10,20 +12,41 @@ module ModBus 
     | 
|
| 
       10 
12 
     | 
    
         
             
              # @see RTUClient#open_connection
         
     | 
| 
       11 
13 
     | 
    
         
             
              # @see Client#with_slave
         
     | 
| 
       12 
14 
     | 
    
         
             
              # @see Slave
         
     | 
| 
       13 
     | 
    
         
            -
              class RTUSlave < Slave
         
     | 
| 
      
 15 
     | 
    
         
            +
              class RTUSlave < Client::Slave
         
     | 
| 
       14 
16 
     | 
    
         
             
                include RTU
         
     | 
| 
       15 
17 
     | 
    
         | 
| 
       16 
18 
     | 
    
         
             
                private
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
       17 
20 
     | 
    
         
             
                # overide method for RTU implamentaion
         
     | 
| 
       18 
21 
     | 
    
         
             
                # @see Slave#query
         
     | 
| 
       19 
22 
     | 
    
         
             
                def send_pdu(pdu)
         
     | 
| 
       20 
     | 
    
         
            -
                   
     | 
| 
      
 23 
     | 
    
         
            +
                  msg = @uid.chr + pdu
         
     | 
| 
      
 24 
     | 
    
         
            +
                  msg << [crc16(msg)].pack("S<")
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                  clean_input_buff
         
     | 
| 
      
 27 
     | 
    
         
            +
                  @io.write msg
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  log "Tx (#{msg.size} bytes): " + logging_bytes(msg)
         
     | 
| 
       21 
30 
     | 
    
         
             
                end
         
     | 
| 
       22 
31 
     | 
    
         | 
| 
       23 
32 
     | 
    
         
             
                # overide method for RTU implamentaion
         
     | 
| 
       24 
33 
     | 
    
         
             
                # @see Slave#query
         
     | 
| 
       25 
34 
     | 
    
         
             
                def read_pdu
         
     | 
| 
       26 
     | 
    
         
            -
                   
     | 
| 
      
 35 
     | 
    
         
            +
                  msg = read_rtu_response(@io)
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                  log "Rx (#{msg.size} bytes): " + logging_bytes(msg)
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                  if msg.getbyte(0) == @uid
         
     | 
| 
      
 40 
     | 
    
         
            +
                    return msg[1..-3] if msg[-2, 2].unpack1("S<") == crc16(msg[0..-3])
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                    log "Ignore package: don't match CRC"
         
     | 
| 
      
 43 
     | 
    
         
            +
                  else
         
     | 
| 
      
 44 
     | 
    
         
            +
                    log "Ignore package: don't match uid ID"
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
                  loop do
         
     | 
| 
      
 47 
     | 
    
         
            +
                    # waite timeout
         
     | 
| 
      
 48 
     | 
    
         
            +
                    sleep(0.1)
         
     | 
| 
      
 49 
     | 
    
         
            +
                  end
         
     | 
| 
       27 
50 
     | 
    
         
             
                end
         
     | 
| 
       28 
51 
     | 
    
         
             
              end
         
     | 
| 
       29 
52 
     | 
    
         
             
            end
         
     | 
| 
         @@ -1,5 +1,7 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            begin
         
     | 
| 
       2 
     | 
    
         
            -
              require  
     | 
| 
      
 4 
     | 
    
         
            +
              require "gserver"
         
     | 
| 
       3 
5 
     | 
    
         
             
            rescue
         
     | 
| 
       4 
6 
     | 
    
         
             
              warn "[WARNING] Install `gserver` gem for use RTUViaTCPServer"
         
     | 
| 
       5 
7 
     | 
    
         
             
            end
         
     | 
| 
         @@ -7,12 +9,13 @@ end 
     | 
|
| 
       7 
9 
     | 
    
         
             
            module ModBus
         
     | 
| 
       8 
10 
     | 
    
         
             
              # RTU over TCP server implementation
         
     | 
| 
       9 
11 
     | 
    
         
             
              # @example
         
     | 
| 
       10 
     | 
    
         
            -
              #   srv = RTUViaTCPServer.new(10002 
     | 
| 
       11 
     | 
    
         
            -
              #    
     | 
| 
       12 
     | 
    
         
            -
              #    
     | 
| 
       13 
     | 
    
         
            -
              #    
     | 
| 
       14 
     | 
    
         
            -
              #    
     | 
| 
       15 
     | 
    
         
            -
              #    
     | 
| 
      
 12 
     | 
    
         
            +
              #   srv = RTUViaTCPServer.new(10002)
         
     | 
| 
      
 13 
     | 
    
         
            +
              #   slave = src.with_slave(1)
         
     | 
| 
      
 14 
     | 
    
         
            +
              #   slave.coils = [1,0,1,1]
         
     | 
| 
      
 15 
     | 
    
         
            +
              #   slave.discrete_inputs = [1,1,0,0]
         
     | 
| 
      
 16 
     | 
    
         
            +
              #   slave.holding_registers = [1,2,3,4]
         
     | 
| 
      
 17 
     | 
    
         
            +
              #   slave.input_registers = [1,2,3,4]
         
     | 
| 
      
 18 
     | 
    
         
            +
              #   srv.logger = Logger.new($stdout)
         
     | 
| 
       16 
19 
     | 
    
         
             
              #   srv.start
         
     | 
| 
       17 
20 
     | 
    
         
             
              class RTUViaTCPServer < GServer
         
     | 
| 
       18 
21 
     | 
    
         
             
                include Debug
         
     | 
| 
         @@ -25,20 +28,10 @@ module ModBus 
     | 
|
| 
       25 
28 
     | 
    
         
             
                # @param [Hash] opts options of server
         
     | 
| 
       26 
29 
     | 
    
         
             
                # @option opts [String] :host host of server default '127.0.0.1'
         
     | 
| 
       27 
30 
     | 
    
         
             
                # @option opts [Float, Integer] :max_connection max of TCP connection with server default 4
         
     | 
| 
       28 
     | 
    
         
            -
                def initialize(port =  
     | 
| 
       29 
     | 
    
         
            -
            			@uid = uid
         
     | 
| 
      
 31 
     | 
    
         
            +
                def initialize(port = 10_002, opts = {})
         
     | 
| 
       30 
32 
     | 
    
         
             
                  opts[:host] = DEFAULT_HOST unless opts[:host]
         
     | 
| 
       31 
33 
     | 
    
         
             
                  opts[:max_connection] = 4 unless opts[:max_connection]
         
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
       33 
     | 
    
         
            -
            		end
         
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
       35 
     | 
    
         
            -
                protected
         
     | 
| 
       36 
     | 
    
         
            -
                # Serve requests
         
     | 
| 
       37 
     | 
    
         
            -
                # @param [TCPSocket] io socket
         
     | 
| 
       38 
     | 
    
         
            -
                def serve(io)
         
     | 
| 
       39 
     | 
    
         
            -
                  serv_rtu_requests(io) do |msg|
         
     | 
| 
       40 
     | 
    
         
            -
                    exec_req(msg[1..-3], @coils, @discrete_inputs, @holding_registers, @input_registers)
         
     | 
| 
       41 
     | 
    
         
            -
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
                  super(port, opts[:host], opts[:max_connection])
         
     | 
| 
       42 
35 
     | 
    
         
             
                end
         
     | 
| 
       43 
36 
     | 
    
         
             
              end
         
     | 
| 
       44 
37 
     | 
    
         
             
            end
         
     | 
| 
         @@ -0,0 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "timeout"
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module ModBus
         
     | 
| 
      
 6 
     | 
    
         
            +
              module Server
         
     | 
| 
      
 7 
     | 
    
         
            +
                class Slave
         
     | 
| 
      
 8 
     | 
    
         
            +
                  attr_accessor :coils, :discrete_inputs, :holding_registers, :input_registers
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  def initialize
         
     | 
| 
      
 11 
     | 
    
         
            +
                    @coils = []
         
     | 
| 
      
 12 
     | 
    
         
            +
                    @discrete_inputs = []
         
     | 
| 
      
 13 
     | 
    
         
            +
                    @holding_registers = []
         
     | 
| 
      
 14 
     | 
    
         
            +
                    @input_registers = []
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     |