Class: ModBus::Client::Slave

Inherits:
Object
  • Object
show all
Includes:
Debug, Errors, Options
Defined in:
lib/rmodbus/client/slave.rb

Direct Known Subclasses

RTUSlave, TCPSlave

Constant Summary collapse

EXCEPTIONS =
{
  1 => IllegalFunction,
  2 => IllegalDataAddress,
  3 => IllegalDataValue,
  4 => SlaveDeviceFailure,
  5 => Acknowledge,
  6 => SlaveDeviceBus,
  8 => MemoryParityError
}.freeze

Instance Attribute Summary collapse

Attributes included from Options

#raise_exception_on_mismatch, #read_retries, #read_retry_timeout

Attributes included from Debug

#logger, #raise_exception_on_mismatch, #read_retries, #read_retry_timeout

Instance Method Summary collapse

Constructor Details

#initialize(uid, io) ⇒ Slave

Returns a new instance of Slave.



24
25
26
27
# File 'lib/rmodbus/client/slave.rb', line 24

def initialize(uid, io)
  @uid = uid
  @io = io
end

Instance Attribute Details

#uidObject

Number of times to retry on read and read timeouts



13
14
15
# File 'lib/rmodbus/client/slave.rb', line 13

def uid
  @uid
end

Instance Method Details

#coilsReadWriteProxy

Returns a ModBus::ReadWriteProxy hash interface for coils

Examples:

coils[addr] => [1]
coils[addr1..addr2] => [1, 0, ..-1]
coils[addr] = 0 => [0]
coils[addr1..addr2] = [1, 0, ..-1] => [1, 0, ..-1]

Returns:



38
39
40
# File 'lib/rmodbus/client/slave.rb', line 38

def coils
  ModBus::ReadWriteProxy.new(self, :coil)
end

#discrete_inputsReadOnlyProxy

Returns a ModBus::ReadOnlyProxy hash interface for discrete inputs

Examples:

discrete_inputs[addr] => [1]
discrete_inputs[addr1..addr2] => [1, 0, ..-1]

Returns:



109
110
111
# File 'lib/rmodbus/client/slave.rb', line 109

def discrete_inputs
  ModBus::ReadOnlyProxy.new(self, :discrete_input)
end

#holding_registersReadWriteProxy

Returns a ModBus::ReadWriteProxy hash interface for holding registers

Examples:

holding_registers[addr] => [123]
holding_registers[addr1..addr2] => [123, 234, ..-1]
holding_registers[addr] = 123 => 123
holding_registers[addr1..addr2] = [234, 345, ..-1] => [234, 345, ..-1]

Returns:



165
166
167
# File 'lib/rmodbus/client/slave.rb', line 165

def holding_registers
  ModBus::ReadWriteProxy.new(self, :holding_register)
end

#input_registersReadOnlyProxy

Returns a read/write ModBus::ReadOnlyProxy hash interface for coils

Examples:

input_registers[addr] => [1]
input_registers[addr1..addr2] => [1, 0, ..-1]

Returns:



136
137
138
# File 'lib/rmodbus/client/slave.rb', line 136

def input_registers
  ModBus::ReadOnlyProxy.new(self, :input_register)
end

#mask_write_register(addr, and_mask, or_mask) ⇒ Object

Mask a holding register

Examples:

mask_write_register(1, 0xAAAA, 0x00FF) => self

Parameters:

  • addr (Integer)

    address registers

  • and_mask (Integer)

    mask for AND operation

  • or_mask (Integer)

    mask for OR operation



222
223
224
225
# File 'lib/rmodbus/client/slave.rb', line 222

def mask_write_register(addr, and_mask, or_mask)
  query("\x16#{addr.to_word}#{and_mask.to_word}#{or_mask.to_word}")
  self
end

#query(request) ⇒ String

Request pdu to slave device

Parameters:

  • pdu (String)

    request to slave

Returns:

Raises:

  • (ResponseMismatch)

    the received echo response differs from the request

  • (ModBusTimeout)

    timed out during read attempt

  • (ModBusException)

    unknown error

  • (IllegalFunction)

    function code received in the query is not an allowable action for the server

  • (IllegalDataAddress)

    data address received in the query is not an allowable address for the server

  • (IllegalDataValue)

    value contained in the query data field is not an allowable value for server

  • (SlaveDeviceFailure)

    unrecoverable error occurred while the server was attempting to perform the requested action

  • (Acknowledge)

    server has accepted the request and is processing it, but a long duration of time will be required to do so

  • (SlaveDeviceBus)

    server is engaged in processing a long duration program command

  • (MemoryParityError)

    extended file area failed to pass a consistency check



263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# File 'lib/rmodbus/client/slave.rb', line 263

def query(request)
  tried = 0
  response = ""
  begin
    ::Timeout.timeout(@read_retry_timeout, ModBusTimeout) do
      send_pdu(request)
      response = read_pdu unless uid.zero?
    end
  rescue ModBusTimeout
    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.empty?

  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_coil(addr) ⇒ Object



54
55
56
# File 'lib/rmodbus/client/slave.rb', line 54

def read_coil(addr)
  read_coils(addr, 1).first
end

#read_coils(addr, ncoils = 1) ⇒ Array

Read coils

Examples:

read_coils(addr, ncoils) => [1, 0, ..-1]

Parameters:

  • addr (Integer)

    address first coil

  • ncoils (Integer) (defaults to: 1)

    number coils

Returns:



50
51
52
# File 'lib/rmodbus/client/slave.rb', line 50

def read_coils(addr, ncoils = 1)
  query("\x01#{addr.to_word}#{ncoils.to_word}").unpack_bits[0..ncoils - 1]
end

#read_discrete_input(addr) ⇒ Object



125
126
127
# File 'lib/rmodbus/client/slave.rb', line 125

def read_discrete_input(addr)
  read_discrete_inputs(addr, 1).first
end

#read_discrete_inputs(addr, ninputs = 1) ⇒ Array

Read discrete inputs

@param ninputs number inputs

Examples:

read_discrete_inputs(addr, ninputs) => [1, 0, ..-1]

Parameters:

  • addr (Integer)

    address first input

Returns:



121
122
123
# File 'lib/rmodbus/client/slave.rb', line 121

def read_discrete_inputs(addr, ninputs = 1)
  query("\x02#{addr.to_word}#{ninputs.to_word}").unpack_bits[0..ninputs - 1]
end

#read_holding_register(addr) ⇒ Object



181
182
183
# File 'lib/rmodbus/client/slave.rb', line 181

def read_holding_register(addr)
  read_holding_registers(addr, 1).first
end

#read_holding_registers(addr, nregs = 1) ⇒ Array

Read holding registers

Examples:

read_holding_registers(1, 5) => [1, 0, ..-1]

Parameters:

  • addr (Integer)

    address first registers

  • nregs (Integer) (defaults to: 1)

    number registers

Returns:



177
178
179
# File 'lib/rmodbus/client/slave.rb', line 177

def read_holding_registers(addr, nregs = 1)
  query("\x03#{addr.to_word}#{nregs.to_word}").unpack("n*")
end

#read_input_register(addr) ⇒ Object



152
153
154
# File 'lib/rmodbus/client/slave.rb', line 152

def read_input_register(addr)
  read_input_registers(addr, 1).first
end

#read_input_registers(addr, nregs = 1) ⇒ Array

Read input registers

Examples:

read_input_registers(1, 5) => [1, 0, ..-1]

Parameters:

  • addr (Integer)

    address first registers

  • nregs (Integer) (defaults to: 1)

    number registers

Returns:



148
149
150
# File 'lib/rmodbus/client/slave.rb', line 148

def read_input_registers(addr, nregs = 1)
  query("\x04#{addr.to_word}#{nregs.to_word}").unpack("n*")
end

#read_write_multiple_registers(addr_r, nregs, addr_w, vals) ⇒ Array Also known as: read_write_holding_registers

Read/write multiple holding registers

Examples:

read_write_multiple_registers(1, 5, 1, [0xaa, 0]) => [1, 0, ..-1]

Parameters:

  • addr_r (Integer)

    address first registers to read

  • nregs (Integer)

    number registers to read

  • addr_w (Integer)

    address first registers to write

  • vals (Array)

    written registers

Returns:



237
238
239
240
241
242
243
# File 'lib/rmodbus/client/slave.rb', line 237

def read_write_multiple_registers(addr_r, nregs, addr_w, vals)
  s_val = vals.map(&:to_word).join

  query("\x17#{addr_r.to_word}#{nregs.to_word}#{addr_w.to_word}" \
        "#{vals.size.to_word}#{(vals.size * 2).chr}#{s_val}")
    .unpack("n*")
end

#write_multiple_coils(addr, vals) ⇒ Object Also known as: write_coils

Write multiple coils

Examples:

write_multiple_coils(1, [0,1,0,1]) => self

Parameters:

  • addr (Integer)

    address first coil

  • vals (Array)

    written coils



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/rmodbus/client/slave.rb', line 83

def write_multiple_coils(addr, vals)
  nbyte = ((vals.size - 1) >> 3) + 1
  sum = 0
  (vals.size - 1).downto(0) do |i|
    sum <<= 1
    sum |= 1 if vals[i].positive?
  end

  s_val = +""
  nbyte.times do
    s_val << (sum & 0xff).chr
    sum >>= 8
  end

  query("\x0f#{addr.to_word}#{vals.size.to_word}#{nbyte.chr}#{s_val}")
  self
end

#write_multiple_registers(addr, vals) ⇒ Object Also known as: write_holding_registers

Write multiple holding registers

Examples:

write_multiple_registers(1, [0xaa, 0]) => self

Parameters:

  • addr (Integer)

    address first registers

  • val (Array)

    written registers

Returns:

  • self



207
208
209
210
211
212
# File 'lib/rmodbus/client/slave.rb', line 207

def write_multiple_registers(addr, vals)
  s_val = vals.map(&:to_word).join

  query("\x10#{addr.to_word}#{vals.size.to_word}#{(vals.size * 2).chr}#{s_val}")
  self
end

#write_single_coil(addr, val) ⇒ Object Also known as: write_coil

Write a single coil

Examples:

write_single_coil(1, 0) => self

Parameters:

  • addr (Integer)

    address coil

  • val (Integer)

    value coil (0 or other)

Returns:

  • self



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

def write_single_coil(addr, val)
  if [0, false].include?(val)
    query("\x05#{addr.to_word}\x00\x00")
  else
    query("\x05#{addr.to_word}\xff\x00")
  end
  self
end

#write_single_register(addr, val) ⇒ Object Also known as: write_holding_register

Write a single holding register

Examples:

write_single_register(1, 0xaa) => self

Parameters:

  • addr (Integer)

    address registers

  • val (Integer)

    written to register

Returns:

  • self



193
194
195
196
# File 'lib/rmodbus/client/slave.rb', line 193

def write_single_register(addr, val)
  query("\x06#{addr.to_word}#{val.to_word}")
  self
end