Module: ModBus::RTU

Included in:
RTUClient, RTUServer, RTUSlave, RTUViaTCPServer
Defined in:
lib/rmodbus/rtu.rb

Instance Method Summary collapse

Instance Method Details

#clean_input_buffObject (private)



32
33
34
35
36
37
38
39
# File 'lib/rmodbus/rtu.rb', line 32

def clean_input_buff
  # empty the input buffer
  if @io.class.public_method_defined? :flush_input
    @io.flush_input
  else
    @io.flush
  end
end

#crc16(msg) ⇒ Object (private)

Calc CRC16 for massage



117
118
119
# File 'lib/rmodbus/rtu.rb', line 117

def crc16(msg)
  Digest::CRC16Modbus.checksum(msg)
end

#read(io, len) ⇒ Object (private)



41
42
43
44
45
46
47
48
49
# File 'lib/rmodbus/rtu.rb', line 41

def read(io, len)
  result = ""
  loop do
    this_iter = io.read(len - result.length)
    result.concat(this_iter) if this_iter
    return result if result.length == len
    io.wait_readable
  end
end

#read_rtu_request(io) ⇒ Object (private)



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/rmodbus/rtu.rb', line 51

def read_rtu_request(io)
	# Every message is a minimum of 4 bytes (slave id, function code, crc16)
	msg = read(io, 4)

	# If msg is nil, then our client never sent us anything and it's time to disconnect
	return if msg.nil?

    loop do
      offset = 0
      crc = msg[-2..-1].unpack("S<").first

      # scan the bytestream for a valid CRC
      loop do
        break if offset >= msg.length - 3
        calculated_crc = Digest::CRC16Modbus.checksum(msg[offset..-3])
        if crc == calculated_crc
          is_response = (msg.getbyte(offset + 1) & 0x80 == 0x80) ||
            (msg.getbyte(offset) == @last_req_uid &&
                msg.getbyte(offset + 1) == @last_req_func &&
            @last_req_timestamp && Time.now.to_f - @last_req_timestamp < 5)

          params = is_response ? parse_response(msg.getbyte(offset + 1), msg[(offset + 1)..-3]) :
              parse_request(msg.getbyte(offset + 1), msg[(offset + 1)..-3])

          unless params.nil?
            if is_response
              @last_req_uid = @last_req_func = @last_req_timestamp = nil
            else
              @last_req_uid = msg.getbyte(offset)
              @last_req_func = msg.getbyte(offset + 1)
              @last_req_timestamp = Time.now.to_f
            end
            log "Server RX discarding #{offset} bytes: #{logging_bytes(msg[0...offset])}" if offset != 0
            log "Server RX (#{msg.size - offset} bytes): #{logging_bytes(msg[offset..-1])}"
            return [msg.getbyte(offset), msg.getbyte(offset + 1), params, msg[offset + 1..-3], is_response]
          end
        end
        offset += 1
      end

      msg.concat(read(io, 1))
      # maximum message size is 256, so that's as far as we have to
      # be able to see at once
      msg = msg[1..-1] if msg.length > 256
    end
end

#read_rtu_response(io) ⇒ Object (private)

We have to read specific amounts of numbers of bytes from the network depending on the function code and content



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/rmodbus/rtu.rb', line 9

def read_rtu_response(io)
 # Read the slave_id and function code
  msg = read(io, 2)      

  function_code = msg.getbyte(1)
  case function_code
    when 1,2,3,4 then
      # read the third byte to find out how much more
      # we need to read + CRC
      msg += read(io, 1)
      msg += read(io, msg.getbyte(2)+2)
    when 5,6,15,16 then
      # We just read in an additional 6 bytes
      msg += read(io, 6)
    when 22 then
      msg += read(io, 8)
    when 0x80..0xff then
      msg += read(io, 3)
    else
      raise ModBus::Errors::IllegalFunction, "Illegal function: #{function_code}"
  end
end

#serve(io) ⇒ Object (private)



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/rmodbus/rtu.rb', line 98

def serve(io)
  loop do
    # read the RTU message
    uid, func, params, pdu, is_response = read_rtu_request(io)

    next if uid.nil?

    pdu = exec_req(uid, func, params, pdu, is_response: is_response)
    next unless pdu

    @last_req_uid = @last_req_func = @last_req_timestamp = nil
    resp = uid.chr + pdu
    resp << [crc16(resp)].pack("S<")
    log "Server TX (#{resp.size} bytes): #{logging_bytes(resp)}"
    io.write resp
  end
end