Module: ModBus::RTU

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

Constant Summary

CHUNK_SIZE =
1500
CrcHiTable =
[
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40]
CrcLoTable =
[
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4,
0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD,
0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7,
0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE,
0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2,
0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB,
0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91,
0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88,
0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80,
0x40]

Instance Method Summary (collapse)

Instance Method Details

- (Object) clean_input_buff (private)



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/rmodbus/rtu.rb', line 49

def clean_input_buff
  win_platform = RUBY_PLATFORM.include? "mingw"
  begin
    # Read up to CHUNK_SIZE bytes of trash.
    if win_platform
      # non-blocking reads are not supported by Windows
      @io.readpartial(CHUNK_SIZE)
    else
      @io.read_nonblock(CHUNK_SIZE)
    end
  rescue Errno::EAGAIN
    # Ignore the fact we couldn't read.
  rescue Exception => e
    raise e unless win_platform && e.is_a?(EOFError) # EOFError means we are done
  end
end

- (Object) crc16(msg) (private)

Calc CRC16 for massage



136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/rmodbus/rtu.rb', line 136

def crc16(msg)
  crc_lo = 0xff
  crc_hi = 0xff

  msg.unpack('c*').each do |byte|
    i = crc_hi ^ byte
    crc_hi = crc_lo ^ CrcHiTable[i]
    crc_lo = CrcLoTable[i]
  end

  return ((crc_hi << 8) + crc_lo)
end

- (Object) read_rtu_pdu (private)



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/rmodbus/rtu.rb', line 76

def read_rtu_pdu
  msg = read_rtu_response(@io)

  log "Rx (#{msg.size} bytes): " + logging_bytes(msg)

  if msg.getbyte(0) == @uid
    return msg[1..-3] if msg[-2,2].unpack('n')[0] == crc16(msg[0..-3])
    log "Ignore package: don't match CRC"
  else
    log "Ignore package: don't match uid ID"
  end
  loop do
    #waite timeout
    sleep(0.1)
  end
end

- (Object) read_rtu_request(io) (private)



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

def read_rtu_request(io)
	# Read the slave_id and function code
	msg = io.read(2)

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

	function_code = msg.getbyte(1)
	if [1, 2, 3, 4, 5, 6].include?(function_code)
		# read 6 more bytes and return the message total message
		msg += io.read(6)
	elsif [15, 16].include?(function_code)
		# Read in first register, register count, and data bytes
		msg += io.read(5)
		# Read in however much data we need to + 2 CRC bytes
		msg += io.read(msg.getbyte(6) + 2)
	else
		raise ModBus::Errors::IllegalFunction, "Illegal function: #{function_code}"
	end

	log "Server RX (#{msg.size} bytes): #{logging_bytes(msg)}"

	msg
end

- (Object) read_rtu_response(io) (private)

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



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/rmodbus/rtu.rb', line 23

def read_rtu_response(io)
 # Read the slave_id and function code
  msg = nil
  while msg.nil?
   msg = io.read(2)
  end

  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 += io.read(1)
      msg += io.read(msg.getbyte(2)+2)
    when 5,6,15,16 then
      # We just read in an additional 6 bytes
      msg += io.read(6)
    when 22 then
      msg += io.read(8)
    when 0x80..0xff then
      msg += io.read(3)
    else
      raise ModBus::Errors::IllegalFunction, "Illegal function: #{function_code}"
  end
end

- (Object) send_rtu_pdu(pdu) (private)



66
67
68
69
70
71
72
73
74
# File 'lib/rmodbus/rtu.rb', line 66

def send_rtu_pdu(pdu)
  msg = @uid.chr + pdu
  msg << crc16(msg).to_word
  
  clean_input_buff  
  @io.write msg

  log "Tx (#{msg.size} bytes): " + logging_bytes(msg)
end

- (Object) serv_rtu_requests(io, &blk) (private)



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/rmodbus/rtu.rb', line 118

def serv_rtu_requests(io, &blk)
  loop do
    # read the RTU message
    msg = read_rtu_request(io)
    # If there is no RTU message, we're done serving this client
    break if msg.nil?

    if msg.getbyte(0) == @uid and msg[-2,2].unpack('n')[0] == crc16(msg[0..-3])
      pdu = yield msg
      resp = @uid.chr + pdu
      resp << crc16(resp).to_word
      log "Server TX (#{resp.size} bytes): #{logging_bytes(resp)}"
      io.write resp
  end
 end
end