class LadderDrive::Protocol::Omron::FinsTcpProtocol

Constants

ETHERNET_CP1E
ETHERNET_CP1H
ETHERNET_CP1L
ETHERNET_ETN21

Available ethernet module.

IOFINS_DESTINATION_NODE_FROM_IP
IOFINS_SOURCE_AUTO_NODE
TIMEOUT

Attributes

destination_network[RW]
destination_node[RW]
destination_unit[RW]
ethernet_module[RW]
gateway_count[RW]
source_network[RW]
source_node[RW]
source_unit[RW]
tcp_error_code[RW]

Public Class Methods

new(options={}) click to toggle source
Calls superclass method
# File lib/ladder_drive/protocol/omron/fins_tcp_protocol.rb, line 53
def initialize options={}
  super
  @socket = nil
  @host = options[:host] || "192.168.250.1"
  @port = options[:port] || 9600
  @gateway_count = 3
  @destination_network = 0
  @destination_node = 0
  @destination_unit = 0
  @source_network = 0
  @source_node = IOFINS_SOURCE_AUTO_NODE
  @source_unit = 0
  @ethernet_module = ETHERNET_ETN21

  @tcp_error_code = 0

  prepare_device_map
end

Public Instance Methods

available_bits_range(device=nil) click to toggle source

max length:

CS1W-ETN21, CJ1W-ETN21   : 2012
CP1W-CIF41 option board  : 540 (1004 if cpu is CP1L/H)
# File lib/ladder_drive/protocol/omron/fins_tcp_protocol.rb, line 236
def available_bits_range device=nil
  case ethernet_module
  when ETHERNET_ETN21
    1..(2012 - 8)
  when ETHERNET_CP1E
    1..(540 - 8)
  when ETHERNET_CP1L, ETHERNET_CP1H
    1..(1004 - 8)
  else
    0..0
  end
end
available_words_range(device=nil) click to toggle source
# File lib/ladder_drive/protocol/omron/fins_tcp_protocol.rb, line 249
def available_words_range device=nil
  case ethernet_module
  when ETHERNET_ETN21
    1..((2012 - 8) / 2)
  when ETHERNET_CP1E
    1..((540 - 8) / 2)
  when ETHERNET_CP1L, ETHERNET_CP1H
    1..((1004 - 8) / 2)
  else
    0..0
  end
end
close() click to toggle source
# File lib/ladder_drive/protocol/omron/fins_tcp_protocol.rb, line 90
def close
  @socket.close if @socket
  @socket = nil
end
create_fins_frame(packet) click to toggle source
# File lib/ladder_drive/protocol/omron/fins_tcp_protocol.rb, line 105
def create_fins_frame packet
  packet = packet.flatten
  header = [ "FINS".bytes.to_a,  0, 0, 0, 0,  0, 0, 0, 2,  0, 0, 0, 0].flatten
  header[4, 4] = int_to_a(packet.length + 8, 4)
  header + packet
end
create_query_node() click to toggle source
# File lib/ladder_drive/protocol/omron/fins_tcp_protocol.rb, line 99
def create_query_node
  header = [ "FINS".bytes.to_a,  0, 0, 0, 0xc,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0].flatten
  header[19] = source_node == IOFINS_SOURCE_AUTO_NODE ? 0 : source_node
  header
end
device_by_name(name) click to toggle source
# File lib/ladder_drive/protocol/omron/fins_tcp_protocol.rb, line 262
def device_by_name name
  case name
  when String
    d = OmronDevice.new name
    d.valid? ? d : nil
  when EscDevice
    local_device_of name
  else
    # it may be already OmronDevice
    name
  end
end
get_bits_from_device(count, device) click to toggle source
# File lib/ladder_drive/protocol/omron/fins_tcp_protocol.rb, line 112
def get_bits_from_device(count, device)
  open
  raise ArgumentError.new("A count #{count} must be between #{available_bits_range.first} and #{available_bits_range.last} for #{__method__}") unless available_bits_range.include? count

  device = device_by_name device
  raise ArgumentError.new("#{device.name} is not bit device!") unless device.bit_device?

  command = [1, 1]
  command << device_to_a(device)
  command << int_to_a(count, 2)

  send_packet create_fins_frame(fins_header + command)
  res = receive

  count.times.inject([]) do |a, i|
    a << (res[16 + 10 + 4 + i] == 0 ? false : true)
    a
  end
end
get_words_from_device(count, device) click to toggle source
# File lib/ladder_drive/protocol/omron/fins_tcp_protocol.rb, line 132
def get_words_from_device(count, device)
  open
  raise ArgumentError.new("A count #{count} must be between #{available_words_range.first} and #{available_words_range.last} for #{__method__}") unless available_words_range.include? count

  device = device_by_name device
  device = device.channel_device

  command = [1, 1]
  command << device_to_a(device)
  command << int_to_a(count, 2)

  send_packet create_fins_frame(fins_header + command)
  res = receive
  count.times.inject([]) do |a, i|
    a << to_int(res[16 + 10 + 4 + i * 2, 2])
    a
  end
end
open() click to toggle source
# File lib/ladder_drive/protocol/omron/fins_tcp_protocol.rb, line 72
    def open
      open!
    rescue =>e
p e
      nil
    end
open!() click to toggle source
# File lib/ladder_drive/protocol/omron/fins_tcp_protocol.rb, line 79
def open!
  if @socket.nil?
    @socket = TCPSocket.open(@host, @port)
    if @socket
      source_node = IOFINS_SOURCE_AUTO_NODE
      query_node
    end
  end
  @socket
end
query_node() click to toggle source
# File lib/ladder_drive/protocol/omron/fins_tcp_protocol.rb, line 189
def query_node
  send_packet create_query_node
  res = receive
  self.source_node = res[19]
end
receive() click to toggle source
# File lib/ladder_drive/protocol/omron/fins_tcp_protocol.rb, line 202
def receive
  res = []
  len = 0
  begin
    Timeout.timeout(TIMEOUT) do
      loop do
        c = @socket.getc
        next if c.nil? || c == ""

        res << c.bytes.first
        next if res.length < 8

        len = to_int(res[4, 4])
        next if res.length < 8 + len

        tcp_command = to_int(res[8, 4])
        case tcp_command
        when 3 # ERROR
          raise "Invalidate tcp header: #{res}"
        end
        break
      end
    end
    raise "Response error code: #{res[15]}" unless res[15] == 0
    res
  end
  @logger.debug("< #{dump_packet res}")
  res
end
send_packet(packet) click to toggle source
# File lib/ladder_drive/protocol/omron/fins_tcp_protocol.rb, line 196
def send_packet packet
  @socket.write(packet.flatten.pack("c*"))
  @socket.flush
  @logger.debug("> #{dump_packet packet}")
end
set_bits_to_device(bits, device) click to toggle source
# File lib/ladder_drive/protocol/omron/fins_tcp_protocol.rb, line 151
def set_bits_to_device(bits, device)
  open
  count = bits.size
  raise ArgumentError.new("A count #{count} must be between #{available_bits_range.first} and #{available_bits_range.last} for #{__method__}") unless available_bits_range.include? count

  device = device_by_name device
  raise ArgumentError.new("#{device.name} is not bit device!") unless device.bit_device?

  command = [1, 2]
  command << device_to_a(device)
  command << int_to_a(count, 2)
  bits.each do |b|
    command << (b ? 1 : 0)
  end

  send_packet create_fins_frame(fins_header + command)
  res = receive
end
set_words_to_device(words, device) click to toggle source
# File lib/ladder_drive/protocol/omron/fins_tcp_protocol.rb, line 170
def set_words_to_device(words, device)
  open
  count = words.size
  raise ArgumentError.new("A count #{count} must be between #{available_words_range.first} and #{available_words_range.last} for #{__method__}") unless available_words_range.include? count

  device = device_by_name device
  device = device.channel_device

  command = [1, 2]
  command << device_to_a(device)
  command << int_to_a(count, 2)
  words.each do |w|
    command << int_to_a(w, 2)
  end

  send_packet create_fins_frame(fins_header + command)
  res = receive
end
tcp_error?() click to toggle source
# File lib/ladder_drive/protocol/omron/fins_tcp_protocol.rb, line 95
def tcp_error?
  tcp_error_code != 0
end

Private Instance Methods

device_code_of(device) click to toggle source
# File lib/ladder_drive/protocol/omron/fins_tcp_protocol.rb, line 309
def device_code_of device
  @@bit_codes ||= { nil => 0x30, "" => 0x30, "W" => 0x31, "H" => 0x32, "A" => 0x33, "T" => 0x09, "C" => 0x09, "D" => 0x02, "E" => 0x0a, "TK" => 0x06 }
  @@word_codes ||= { nil => 0xB0, "" => 0xB0, "W" => 0xB1, "H" => 0xB2, "A" => 0xB3, "TIM" => 0x89, "CNT" => 0x89, "D" => 0x82, "E" => 0x98, "DR" => 0xbc }
  if device.bit_device?
    @@bit_codes[device.suffix]
  else
    @@word_codes[device.suffix]
  end
end
device_to_a(device) click to toggle source
# File lib/ladder_drive/protocol/omron/fins_tcp_protocol.rb, line 319
def device_to_a device
  a = []
  a << device_code_of(device)
  a << int_to_a(device.channel, 2)
  a << (device.bit_device? ? (device.bit || 0) : 0)
  a.flatten
end
dump_packet(packet) click to toggle source
# File lib/ladder_drive/protocol/omron/fins_tcp_protocol.rb, line 368
def dump_packet packet
  a =
    packet.map{|e|
      e.to_s(16).rjust(2, '0')
    }
  "[#{a.join(', ')}]"
end
fins_header() click to toggle source
# File lib/ladder_drive/protocol/omron/fins_tcp_protocol.rb, line 277
def fins_header
  buf = [
          0x80, # ICF
          0x00, # RSV
          0x02, # GCT
          0x00, # DNA
          0x01, # DA1
          0x00, # DA2
          0x00, # SNA
          0x01, # SA1
          0x00, # SA2
          0x00, # SID
        ]
  buf[2] = gateway_count - 1
  buf[3] = destination_network
  if destination_node == IOFINS_DESTINATION_NODE_FROM_IP
    buf[4] = destination_ipv4.split(".").last.to_i
  else
    buf[4] = destination_node
  end
  buf[7] = source_node
  buf[8] = source_unit

  buf
end
fins_tcp_cmnd_header() click to toggle source
# File lib/ladder_drive/protocol/omron/fins_tcp_protocol.rb, line 303
def fins_tcp_cmnd_header
  header = [ "FINS".bytes.to_a,  0, 0, 0, 0xc,  0, 0, 0, 2,  0, 0, 0, 0].flatten
  header[19] = source_node == IOFINS_SOURCE_AUTO_NODE ? 0 : source_node
  header
end
int_to_a(value, size) click to toggle source
# File lib/ladder_drive/protocol/omron/fins_tcp_protocol.rb, line 351
def int_to_a value, size
  a = []
  (size - 1).downto 0 do |i|
    a << ((value >> (i * 8)) & 0xff)
  end
  a
end
prepare_device_map() click to toggle source

FIXME: It's dummy currently.

# File lib/ladder_drive/protocol/omron/fins_tcp_protocol.rb, line 329
def prepare_device_map
  @conv_dev_dict ||= begin
    h = {}
    [
      ["X", "0.0", 1024],
      ["Y", "400.0", 1024],
      ["M", "M0.0", 1024],
      ["C", "C0", 256],
      ["T", "T0", 256],
      ["L", "H0.0", 1024],
      ["SC", "M400.0", 1024],
      ["D", "D0", 1024],
      ["H", "D1024", 1024],
      ["SD", "D2048", 1024],
      ["PRG", "D3072", 1024]    # ..D4095
    ].each do |s,d,c|
      h[s] = [OmronDevice.new(d), c]
    end
    h
  end
end
to_int(a) click to toggle source
# File lib/ladder_drive/protocol/omron/fins_tcp_protocol.rb, line 359
def to_int a
  v = 0
  a.each do |e|
    v <<= 8
    v += e
  end
  v
end