module ModBus::RTU
Private Instance Methods
clean_input_buff()
click to toggle source
# 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)
click to toggle source
Calc CRC16 for massage
# File lib/rmodbus/rtu.rb, line 117 def crc16(msg) Digest::CRC16Modbus.checksum(msg) end
read(io, len)
click to toggle source
# 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)
click to toggle source
# 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)
click to toggle source
We have to read specific amounts of numbers of bytes from the network depending on the function code and content
# 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)
click to toggle source
# 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