class ModBus::Slave
Constants
- Exceptions
Attributes
Number of times to retry on read and read timeouts
Public Class Methods
# File lib/rmodbus/slave.rb, line 19 def initialize(uid, io) @uid = uid @io = io end
Public Instance Methods
Returns a ModBus::ReadWriteProxy
hash interface for coils
@example
coils[addr] => [1] coils[addr1..addr2] => [1, 0, ..] coils[addr] = 0 => [0] coils[addr1..addr2] = [1, 0, ..] => [1, 0, ..]
@return [ReadWriteProxy] proxy object
# File lib/rmodbus/slave.rb, line 33 def coils ModBus::ReadWriteProxy.new(self, :coil) end
Returns a ModBus::ReadOnlyProxy
hash interface for discrete inputs
@example
discrete_inputs[addr] => [1] discrete_inputs[addr1..addr2] => [1, 0, ..]
@return [ReadOnlyProxy] proxy object
# File lib/rmodbus/slave.rb, line 101 def discrete_inputs ModBus::ReadOnlyProxy.new(self, :discrete_input) end
Returns a ModBus::ReadWriteProxy
hash interface for holding registers
@example
holding_registers[addr] => [123] holding_registers[addr1..addr2] => [123, 234, ..] holding_registers[addr] = 123 => 123 holding_registers[addr1..addr2] = [234, 345, ..] => [234, 345, ..]
@return [ReadWriteProxy] proxy object
# File lib/rmodbus/slave.rb, line 151 def holding_registers ModBus::ReadWriteProxy.new(self, :holding_register) end
Returns a read/write ModBus::ReadOnlyProxy
hash interface for coils
@example
input_registers[addr] => [1] input_registers[addr1..addr2] => [1, 0, ..]
@return [ReadOnlyProxy] proxy object
# File lib/rmodbus/slave.rb, line 125 def input_registers ModBus::ReadOnlyProxy.new(self, :input_register) end
Mask a holding register
@example
mask_write_register(1, 0xAAAA, 0x00FF) => self
@param [Integer] addr address registers @param [Integer] and_mask mask for AND operation @param [Integer] or_mask mask for OR operation
# File lib/rmodbus/slave.rb, line 209 def mask_write_register(addr, and_mask, or_mask) query("\x16" + addr.to_word + and_mask.to_word + or_mask.to_word) self end
Request pdu to slave device
@param [String] pdu request to slave @return [String] received data
@raise [ResponseMismatch] the received echo response differs from the request @raise [ModBusTimeout] timed out during read attempt @raise [ModBusException] unknown error @raise [IllegalFunction] function code received in the query is not an allowable action for the server @raise [IllegalDataAddress] data address received in the query is not an allowable address for the server @raise [IllegalDataValue] value contained in the query data field is not an allowable value for server @raise [SlaveDeviceFailure] unrecoverable error occurred while the server was attempting to perform the requested action @raise [Acknowledge] server has accepted the request and is processing it, but a long duration of time will be required to do so @raise [SlaveDeviceBus] server is engaged in processing a long duration program command @raise [MemoryParityError] extended file area failed to pass a consistency check
# File lib/rmodbus/slave.rb, line 229 def query(request) tried = 0 response = "" begin ::Timeout.timeout(@read_retry_timeout, ModBusTimeout) do send_pdu(request) response = read_pdu end rescue ModBusTimeout => err log "Timeout of read operation: (#{@read_retries - tried})" tried += 1 retry unless tried >= @read_retries raise ModBusTimeout.new, "Timed out during read attempt" end return nil if response.size == 0 read_func = response.getbyte(0) if read_func >= 0x80 exc_id = response.getbyte(1) raise Exceptions[exc_id] unless Exceptions[exc_id].nil? raise ModBusException.new, "Unknown error" end check_response_mismatch(request, response) if raise_exception_on_mismatch response[2..-1] end
Read coils
@example
read_coils(addr, ncoils) => [1, 0, ..]
@param [Integer] addr address first coil @param [Integer] ncoils number coils @return [Array] coils
# File lib/rmodbus/slave.rb, line 45 def read_coils(addr, ncoils) query("\x1" + addr.to_word + ncoils.to_word).unpack_bits[0..ncoils-1] end
Read discrete inputs
@example
read_discrete_inputs(addr, ninputs) => [1, 0, ..]
@param [Integer] addr address first input @param ninputs number inputs @return [Array] inputs
# File lib/rmodbus/slave.rb, line 113 def read_discrete_inputs(addr, ninputs) query("\x2" + addr.to_word + ninputs.to_word).unpack_bits[0..ninputs-1] end
Read holding registers
@example
read_holding_registers(1, 5) => [1, 0, ..]
@param [Integer] addr address first registers @param [Integer] nregs number registers @return [Array] registers
# File lib/rmodbus/slave.rb, line 163 def read_holding_registers(addr, nregs) query("\x3" + addr.to_word + nregs.to_word).unpack('n*') end
Read input registers
@example
read_input_registers(1, 5) => [1, 0, ..]
@param [Integer] addr address first registers @param [Integer] nregs number registers @return [Array] registers
# File lib/rmodbus/slave.rb, line 137 def read_input_registers(addr, nregs) query("\x4" + addr.to_word + nregs.to_word).unpack('n*') end
Write multiple coils
@example
write_multiple_coils(1, [0,1,0,1]) => self
@param [Integer] addr address first coil @param [Array] vals written coils
# File lib/rmodbus/slave.rb, line 75 def write_multiple_coils(addr, vals) nbyte = ((vals.size-1) >> 3) + 1 sum = 0 (vals.size - 1).downto(0) do |i| sum = sum << 1 sum |= 1 if vals[i] > 0 end s_val = "" nbyte.times do s_val << (sum & 0xff).chr sum >>= 8 end query("\xf" + addr.to_word + vals.size.to_word + nbyte.chr + s_val) self end
Write multiple holding registers
@example
write_multiple_registers(1, [0xaa, 0]) => self
@param [Integer] addr address first registers @param [Array] val written registers @return self
# File lib/rmodbus/slave.rb, line 191 def write_multiple_registers(addr, vals) s_val = "" vals.each do |reg| s_val << reg.to_word end query("\x10" + addr.to_word + vals.size.to_word + (vals.size * 2).chr + s_val) self end
Write a single coil
@example
write_single_coil(1, 0) => self
@param [Integer] addr address coil @param [Integer] val value coil (0 or other) @return self
# File lib/rmodbus/slave.rb, line 58 def write_single_coil(addr, val) if val == 0 query("\x5" + addr.to_word + 0.to_word) else query("\x5" + addr.to_word + 0xff00.to_word) end self end
Write a single holding register
@example
write_single_register(1, 0xaa) => self
@param [Integer] addr address registers @param [Integer] val written to register @return self
# File lib/rmodbus/slave.rb, line 176 def write_single_register(addr, val) query("\x6" + addr.to_word + val.to_word) self end
Private Instance Methods
# File lib/rmodbus/slave.rb, line 259 def check_response_mismatch(request, response) read_func = response.getbyte(0) data = response[2..-1] #Mismatch functional code send_func = request.getbyte(0) if read_func != send_func msg = "Function code is mismatch (expected #{send_func}, got #{read_func})" end case read_func when 1,2 bc = request.getword(3)/8 + 1 if data.size != bc msg = "Byte count is mismatch (expected #{bc}, got #{data.size} bytes)" end when 3,4 rc = request.getword(3) if data.size/2 != rc msg = "Register count is mismatch (expected #{rc}, got #{data.size/2} regs)" end when 5,6 exp_addr = request.getword(1) got_addr = response.getword(1) if exp_addr != got_addr msg = "Address is mismatch (expected #{exp_addr}, got #{got_addr})" end exp_val = request.getword(3) got_val = response.getword(3) if exp_val != got_val msg = "Value is mismatch (expected 0x#{exp_val.to_s(16)}, got 0x#{got_val.to_s(16)})" end when 15,16 exp_addr = request.getword(1) got_addr = response.getword(1) if exp_addr != got_addr msg = "Address is mismatch (expected #{exp_addr}, got #{got_addr})" end exp_quant = request.getword(3) got_quant = response.getword(3) if exp_quant != got_quant msg = "Quantity is mismatch (expected #{exp_quant}, got #{got_quant})" end else warn "Fuiction (#{read_func}) is not supported raising response mismatch" end raise ResponseMismatch.new(msg, request, response) if msg end