Class: ModBus::Slave

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

Direct Known Subclasses

RTUSlave, RTUViaTCPSlave, TCPSlave

Constant Summary collapse

Exceptions =
{
      1 => IllegalFunction.new("The function code received in the query is not an allowable action for the server"),
      2 => IllegalDataAddress.new("The data address received in the query is not an allowable address for the server"),
      3 => IllegalDataValue.new("A value contained in the query data field is not an allowable value for server"),
      4 => SlaveDeviceFailure.new("An unrecoverable error occurred while the server was attempting to perform the requested action"),
      5 => Acknowledge.new("The server has accepted the request and is processing it, but a long duration of time will be required to do so"),
      6 => SlaveDeviceBus.new("The server is engaged in processing a long duration program command"),
      8 => MemoryParityError.new("The extended file area failed to pass a consistency check")
}

Instance Attribute Summary collapse

Attributes included from Options

#raise_exception_on_mismatch, #read_retries, #read_retry_timeout

Attributes included from Debug

#debug, #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.



17
18
19
20
# File 'lib/rmodbus/slave.rb', line 17

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

Instance Attribute Details

#uidObject

Number of times to retry on read and read timeouts



7
8
9
# File 'lib/rmodbus/slave.rb', line 7

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, ..]
coils[addr] = 0 => [0]
coils[addr1..addr2] = [1, 0, ..] => [1, 0, ..]

Returns:



31
32
33
# File 'lib/rmodbus/slave.rb', line 31

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, ..]

Returns:



99
100
101
# File 'lib/rmodbus/slave.rb', line 99

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, ..]
holding_registers[addr] = 123 => 123
holding_registers[addr1..addr2] = [234, 345, ..] => [234, 345, ..]

Returns:



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

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, ..]

Returns:



123
124
125
# File 'lib/rmodbus/slave.rb', line 123

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



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

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



227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/rmodbus/slave.rb', line 227

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(addr, ncoils) ⇒ Array Also known as: read_coil

Read coils

Examples:

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

Parameters:

  • addr (Integer)

    address first coil

  • ncoils (Integer)

    number coils

Returns:



43
44
45
# File 'lib/rmodbus/slave.rb', line 43

def read_coils(addr, ncoils)
  query("\x1" + addr.to_word + ncoils.to_word).unpack_bits[0..ncoils-1]
end

#read_discrete_inputs(addr, ninputs) ⇒ Array Also known as: read_discrete_input

Read discrete inputs

@param ninputs number inputs

Examples:

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

Parameters:

  • addr (Integer)

    address first input

Returns:



111
112
113
# File 'lib/rmodbus/slave.rb', line 111

def read_discrete_inputs(addr, ninputs)
  query("\x2" + addr.to_word + ninputs.to_word).unpack_bits[0..ninputs-1]
end

#read_holding_registers(addr, nregs) ⇒ Array Also known as: read_holding_register

Read holding registers

Examples:

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

Parameters:

  • addr (Integer)

    address first registers

  • nregs (Integer)

    number registers

Returns:



161
162
163
# File 'lib/rmodbus/slave.rb', line 161

def read_holding_registers(addr, nregs)
  query("\x3" + addr.to_word + nregs.to_word).unpack('n*')
end

#read_input_registers(addr, nregs) ⇒ Array Also known as: read_input_register

Read input registers

Examples:

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

Parameters:

  • addr (Integer)

    address first registers

  • nregs (Integer)

    number registers

Returns:



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

def read_input_registers(addr, nregs)
  query("\x4" + addr.to_word + nregs.to_word).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



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/rmodbus/slave.rb', line 73

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_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



189
190
191
192
193
194
195
196
197
# File 'lib/rmodbus/slave.rb', line 189

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_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



56
57
58
59
60
61
62
63
# File 'lib/rmodbus/slave.rb', line 56

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_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



174
175
176
177
# File 'lib/rmodbus/slave.rb', line 174

def write_single_register(addr, val)
  query("\x6" + addr.to_word + val.to_word)
  self
end