Class: PlcAccess::Protocol::Mitsubishi::FxProtocol

Inherits:
Protocol
  • Object
show all
Defined in:
lib/plc_access/protocol/mitsubishi/fx_protocol.rb

Constant Summary collapse

STX =
"\u0002"
ETX =
"\u0003"
EOT =
"\u0004"
ENQ =
"\u0005"
ACK =
"\u0006"
LF =
"\u000a"
CR =
"\u000d"
NAK =
"\u0015"
DELIMITER =
"\r\n"

Constants inherited from Protocol

Protocol::TIMEOUT

Instance Attribute Summary collapse

Attributes inherited from Protocol

#host, #log_level, #port

Instance Method Summary collapse

Methods inherited from Protocol

#[], #[]=, #destination_ipv4, #get_bit_from_device, #get_from_devices, #get_word_from_device, #self_ipv4, #set_bit_to_device, #set_to_devices, #set_word_to_device

Constructor Details

#initialize(options = {}) ⇒ FxProtocol

Returns a new instance of FxProtocol.



43
44
45
46
47
48
49
50
51
# File 'lib/plc_access/protocol/mitsubishi/fx_protocol.rb', line 43

def initialize(options = {})
  super
  @port = options[:port] || `ls /dev/tty.usb*`.split("\n").map(&:chomp).first
  @pc_no = 0xff
  @baudrate = 19_200
  @station_no = 0
  @wait_time = 0
  @comm = nil
end

Instance Attribute Details

#baudrateObject

Returns the value of attribute baudrate.



30
31
32
# File 'lib/plc_access/protocol/mitsubishi/fx_protocol.rb', line 30

def baudrate
  @baudrate
end

#pc_noObject

Returns the value of attribute pc_no.



30
31
32
# File 'lib/plc_access/protocol/mitsubishi/fx_protocol.rb', line 30

def pc_no
  @pc_no
end

#station_noObject

Returns the value of attribute station_no.



30
31
32
# File 'lib/plc_access/protocol/mitsubishi/fx_protocol.rb', line 30

def station_no
  @station_no
end

#wait_timeObject

Returns the value of attribute wait_time.



30
31
32
# File 'lib/plc_access/protocol/mitsubishi/fx_protocol.rb', line 30

def wait_time
  @wait_time
end

Instance Method Details

#available_bits_range(_device = nil) ⇒ Object



198
199
200
# File 'lib/plc_access/protocol/mitsubishi/fx_protocol.rb', line 198

def available_bits_range(_device = nil)
  1..256
end

#available_words_range(_device = nil) ⇒ Object



202
203
204
# File 'lib/plc_access/protocol/mitsubishi/fx_protocol.rb', line 202

def available_words_range(_device = nil)
  1..64
end

#body_for_get_bit_from_deivce(device) ⇒ Object



206
207
208
# File 'lib/plc_access/protocol/mitsubishi/fx_protocol.rb', line 206

def body_for_get_bit_from_deivce(device)
  body_for_get_bits_from_device 1, device
end

#body_for_get_bits_from_device(count, device) ⇒ Object



210
211
212
213
214
215
216
# File 'lib/plc_access/protocol/mitsubishi/fx_protocol.rb', line 210

def body_for_get_bits_from_device(count, device)
  body = header_with_command 'BR'
  body += "#{device.name}#{count.to_s(16).rjust(2, '0')}"
  body += check_sum(body)
  body += DELIMITER
  body.upcase
end

#body_for_get_words_from_device(count, device) ⇒ Object



218
219
220
221
222
223
224
# File 'lib/plc_access/protocol/mitsubishi/fx_protocol.rb', line 218

def body_for_get_words_from_device(count, device)
  body = header_with_command 'WR'
  body += "#{device.name}#{count.to_s(16).rjust(2, '0')}"
  body += check_sum(body)
  body += DELIMITER
  body.upcase
end

#body_for_set_bits_to_device(bits, device) ⇒ Object Also known as: body_for_set_bit_to_device



226
227
228
229
230
231
232
233
# File 'lib/plc_access/protocol/mitsubishi/fx_protocol.rb', line 226

def body_for_set_bits_to_device(bits, device)
  body = header_with_command 'BW'
  body += "#{device.name}#{bits.count.to_s(16).rjust(2, '0')}"
  body += bits.map { |b| b ? '1' : '0' }.join('')
  body += check_sum(body)
  body += DELIMITER
  body.upcase
end

#body_for_set_words_to_device(words, device) ⇒ Object



236
237
238
239
240
241
242
243
# File 'lib/plc_access/protocol/mitsubishi/fx_protocol.rb', line 236

def body_for_set_words_to_device(words, device)
  body = header_with_command 'WW'
  body += "#{device.name}#{words.count.to_s(16).rjust(2, '0')}"
  body += words.map { |w| w.to_s(16).rjust(4, '0') }.join('')
  body += check_sum(body)
  body += DELIMITER
  body.upcase
end

#closeObject



74
75
76
77
# File 'lib/plc_access/protocol/mitsubishi/fx_protocol.rb', line 74

def close
  @comm&.close
  @comm = nil
end

#device_by_name(name) ⇒ Object



173
174
175
176
177
178
179
180
181
182
# File 'lib/plc_access/protocol/mitsubishi/fx_protocol.rb', line 173

def device_by_name(name)
  case name
  when String
    d = FxDevice.new name
    d.valid? ? d : nil
  else
    # it may be already QDevice
    name
  end
end

#dump_packet(packet) ⇒ Object



245
246
247
# File 'lib/plc_access/protocol/mitsubishi/fx_protocol.rb', line 245

def dump_packet(packet)
  packet.inspect
end

#get_bits_from_device(count, device) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/plc_access/protocol/mitsubishi/fx_protocol.rb', line 79

def get_bits_from_device(count, device)
  unless available_bits_range.include? count
    raise ArgumentError,
          "A count #{count} must be between #{available_bits_range.first} and #{available_bits_range.last} for #{__method__}"
  end

  device = device_by_name device
  packet = body_for_get_bits_from_device(count, device) + DELIMITER
  @logger.debug("> #{dump_packet packet}")
  open
  @comm.write(packet)
  @comm.flush
  res = receive
  bits = []

  if res && (check_sum(res[0..-5]) == res[-4, 2])
    bits =
      res[5..-6].each_char.map do |c|
        c == '1'
      end
  end
  @logger.debug("> #{dump_packet ack_packet}")
  @comm.write(ack_packet)
  @logger.debug("get #{device.name} => #{bits}")

  bits
end

#get_words_from_device(count, device) ⇒ Object



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/plc_access/protocol/mitsubishi/fx_protocol.rb', line 126

def get_words_from_device(count, device)
  unless available_words_range.include? count
    raise ArgumentError,
          "A count #{count} must be between #{available_words_range.first} and #{available_words_range.last} for #{__method__}"
  end

  device = device_by_name device
  packet = body_for_get_words_from_device(count, device) + DELIMITER
  @logger.debug("> #{dump_packet packet}")
  open
  @comm.write(packet)
  @comm.flush
  res = receive
  words = []

  if res && (check_sum(res[0..-5]) == res[-4, 2])
    words =
      res[5..-6].scan(/.{4}/).map do |v|
        v.to_i(16)
      end
  end
  @logger.debug("> #{dump_packet ack_packet}")
  @comm.write(ack_packet)
  @logger.debug("get #{device.name} => #{words}")

  words
end

#openObject



53
54
55
56
57
# File 'lib/plc_access/protocol/mitsubishi/fx_protocol.rb', line 53

def open
  open!
rescue StandardError
  nil
end

#open!Object



59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/plc_access/protocol/mitsubishi/fx_protocol.rb', line 59

def open!
  return false unless @port

  begin
    # port, baudrate, bits, stop bits, parity(0:none, 1:even, 2:odd)
    @comm ||= SerialPort.new(@port, @baudrate, 7, 1, 2).tap do |s|
      s.read_timeout = (TIMEOUT * 1000.0).to_i
    end
    raise StandardError.new("invalid port #{@port}") unless @comm
  rescue StandardError => e
    p e
    nil
  end
end

#receiveObject



184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/plc_access/protocol/mitsubishi/fx_protocol.rb', line 184

def receive
  res = ''
  begin
    Timeout.timeout(TIMEOUT) do
      res = @comm.gets
    end
    res
  rescue Timeout::Error
    puts "*** ERROR: TIME OUT : #{res} ***"
  end
  @logger.debug("< #{dump_packet res}")
  res
end

#set_bits_to_device(bits, device) ⇒ Object



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/plc_access/protocol/mitsubishi/fx_protocol.rb', line 107

def set_bits_to_device(bits, device)
  unless available_bits_range.include? bits.size
    raise ArgumentError,
          "A count #{count} must be between #{available_bits_range.first} and #{available_bits_range.last} for #{__method__}"
  end

  device = device_by_name device
  packet = body_for_set_bits_to_device(bits, device)
  @logger.debug("> #{dump_packet packet}")
  open
  @comm.write(packet)
  @comm.flush
  res = receive
  @logger.debug("set #{bits} to:#{device.name}")

  # error checking
  raise "ERROR: return #{res} for set_bits_to_device(#{bits}, #{device.name})" unless res == ack_packet
end

#set_words_to_device(words, device) ⇒ Object



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/plc_access/protocol/mitsubishi/fx_protocol.rb', line 154

def set_words_to_device(words, device)
  unless available_bits_range.include? words.size
    raise ArgumentError,
          "A count of words #{words.size} must be between #{available_words_range.first} and #{available_words_range.last} for #{__method__}"
  end

  device = device_by_name device
  packet = body_for_set_words_to_device(words, device)
  @logger.debug("> #{dump_packet packet}")
  open
  @comm.write(packet)
  @comm.flush
  res = receive
  @logger.debug("set #{words} to: #{device.name}")

  # error checking
  raise "ERROR: return #{res} for set_bits_to_device(#{words}, #{device.name})" unless res == ack_packet
end