Class: OpenHAB::Core::IO::Transport::Serial::SerialPort

Inherits:
Object
  • Object
show all
Defined in:
lib/openhab/core/io/transport/serial/serial_port.rb

Overview

IO-like access to an openHAB serial port.

Examples:

OpenHAB::Core::IO::Transport::Serial::SerialPort.open("/dev/ttyUSB0",
                                                      baud: 9600,
                                                      data_bits: 8,
                                                      parity: :none,
                                                      stop_bits: 1) do |port|
  port.write("hello\r\n")
  port.read(1024) if port.wait_readable(1)
end

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#baud ⇒ Integer

Returns:



302
303
304
305
# File 'lib/openhab/core/io/transport/serial/serial_port.rb', line 302

def baud
  check_open
  @configuration ? @configuration[0] : @port.baud_rate
end

#data_bits ⇒ 5, ...

Returns:

  • (5, 6, 7, 8)


315
316
317
318
# File 'lib/openhab/core/io/transport/serial/serial_port.rb', line 315

def data_bits
  check_open
  @configuration ? @configuration[1] : @port.data_bits
end

#name ⇒ String (readonly)

Returns The port name.

Returns:

  • (String) —

    The port name.



53
54
55
# File 'lib/openhab/core/io/transport/serial/serial_port.rb', line 53

def name
  @name
end

#parity ⇒ :none, ...

Returns:

  • (:none, :odd, :even, :mark, :space)


342
343
344
345
# File 'lib/openhab/core/io/transport/serial/serial_port.rb', line 342

def parity
  check_open
  PARITIES.key(@configuration ? @configuration[3] : @port.parity)
end

#stop_bits ⇒ 1, ...

Returns:

  • (1, 1.5, 2)


328
329
330
331
332
# File 'lib/openhab/core/io/transport/serial/serial_port.rb', line 328

def stop_bits
  check_open
  value = @configuration ? @configuration[2] : @port.stop_bits
  (value == 3) ? 1.5 : value
end

Class Method Details

.open(name, baud: nil, data_bits: nil, parity: nil, stop_bits: nil, owner: "org.openhab.automation.jrubyscripting", timeout: 2000) {|port| ... } ⇒ SerialPort, Object

Opens a port and ensures it is closed after an optional block.

Parameters:

  • name (String) —

    Port name or provider URI.

  • baud (Integer, nil) (defaults to: nil) —

    Baud rate; nil preserves the provider default.

  • data_bits (5, 6, 7, 8, nil) (defaults to: nil)
  • parity (:none, :odd, :even, :mark, :space, nil) (defaults to: nil)
  • stop_bits (1, 1.5, 2, nil) (defaults to: nil)
  • owner (String) (defaults to: "org.openhab.automation.jrubyscripting") —

    Owner that should be reported by openHAB.

  • timeout (Integer) (defaults to: 2000) —

    Port acquisition timeout in milliseconds.

Yield Parameters:

Returns:

  • (SerialPort, Object) —

    The port if a block is not given, otherwise the result of the block.



34
35
36
37
38
39
40
41
42
43
# File 'lib/openhab/core/io/transport/serial/serial_port.rb', line 34

def open(...)
  port = new(...)
  return port unless block_given?

  begin
    yield port
  ensure
    port.close
  end
end

Instance Method Details

#<<(string) ⇒ self

Parameters:

  • string (String)

Returns:

  • (self)


237
238
239
240
# File 'lib/openhab/core/io/transport/serial/serial_port.rb', line 237

def <<(string)
  write(string)
  self
end

#close ⇒ nil

Releases the underlying serial port. Repeated calls are harmless.

Returns:

  • (nil)


253
254
255
256
257
258
259
260
# File 'lib/openhab/core/io/transport/serial/serial_port.rb', line 253

def close
  return if closed?

  @closed = true
  @port.close
  ObjectSpace.undefine_finalizer(self)
  nil
end

#closed? ⇒ Boolean

Returns:

  • (Boolean)


263
264
265
# File 'lib/openhab/core/io/transport/serial/serial_port.rb', line 263

def closed?
  !!@closed
end

#configure {|port| ... } ⇒ self

Applies several settings at once. Failed blocks do not apply settings.

Examples:

port.configure do
  port.baud = 9600
  port.data_bits = 8
  port.parity = :none
  port.stop_bits = 1
end

Yield Parameters:

  • port (self)

Returns:

  • (self)


279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/openhab/core/io/transport/serial/serial_port.rb', line 279

def configure
  check_open
  previous = @configuration
  @configuration = if previous
                     previous.dup
                   else
                     [@port.baud_rate,
                      @port.data_bits,
                      @port.stop_bits,
                      @port.parity]
                   end
  begin
    yield self
    @port.set_serial_port_params(*@configuration) unless previous
    previous&.replace(@configuration)
  ensure
    @configuration = previous
  end
  self
end

#flush ⇒ self

Returns:

  • (self)


243
244
245
246
247
248
249
# File 'lib/openhab/core/io/transport/serial/serial_port.rb', line 243

def flush
  check_open
  @output.flush
  self
rescue java.io.IOException => e
  raise IOError, e.message
end

#getbyte ⇒ Integer?

Returns The next available byte.

Returns:

  • (Integer, nil) —

    The next available byte.



213
214
215
# File 'lib/openhab/core/io/transport/serial/serial_port.rb', line 213

def getbyte
  read(1)&.getbyte(0)
end

#inspect ⇒ String

Returns:

  • (String)


352
353
354
355
356
# File 'lib/openhab/core/io/transport/serial/serial_port.rb', line 352

def inspect
  return "#<#{self.class.name}:#{name} (closed)>" if closed?

  "#<#{self.class.name}:#{name} #{baud} #{data_bits}#{parity.to_s[0].upcase}#{stop_bits}>"
end

Writes objects using Ruby IO formatting and output separators.

Parameters:

  • objects (Array<Object>)

Returns:

  • (nil)


128
129
130
# File 'lib/openhab/core/io/transport/serial/serial_port.rb', line 128

def print(*objects)
  formatted_write(:print, *objects)
end

#printf(format, *objects) ⇒ nil

Writes a formatted string.

Parameters:

  • format (String)
  • objects (Array<Object>)

Returns:

  • (nil)


136
137
138
# File 'lib/openhab/core/io/transport/serial/serial_port.rb', line 136

def printf(format, *objects)
  formatted_write(:printf, format, *objects)
end

#puts(*objects) ⇒ nil

Writes objects followed by newlines, using Ruby IO formatting.

Parameters:

  • objects (Array<Object>)

Returns:

  • (nil)


121
122
123
# File 'lib/openhab/core/io/transport/serial/serial_port.rb', line 121

def puts(*objects)
  formatted_write(:puts, *objects)
end

#read(length, outbuf = nil) ⇒ String?

Reads up to length bytes currently available, or nil if none are ready.

Parameters:

  • length (Integer)
  • outbuf (String, nil) (defaults to: nil)

Returns:

  • (String, nil)


144
145
146
147
# File 'lib/openhab/core/io/transport/serial/serial_port.rb', line 144

def read(length, outbuf = nil)
  result = read_nonblock(length, outbuf, exception: false)
  (result == :wait_readable) ? nil : result
end

#read_nonblock(length, outbuf = nil, exception: true) ⇒ String, ...

Parameters:

  • length (Integer)
  • outbuf (String, nil) (defaults to: nil)
  • exception (Boolean) (defaults to: true) —

    Raise IO::EAGAINWaitReadable if no bytes are ready, or EOFError at EOF.

Returns:



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/openhab/core/io/transport/serial/serial_port.rb', line 153

def read_nonblock(length, outbuf = nil, exception: true)
  check_open
  length = length.to_int
  raise ArgumentError, "negative length" if length.negative?

  return readpartial(0, outbuf) if length.zero?

  available = @input.available
  if available.zero?
    outbuf&.clear
    return :wait_readable unless exception

    raise ::IO::EAGAINWaitReadable, "read would block"
  end

  result = readpartial(0, outbuf)
  length = [length, available].min
  bytes = Java::byte[length].new
  count = @input.read(bytes, 0, length)
  return nil if count.negative? && !exception

  raise EOFError, "end of file reached" if count.negative?
  return result.replace(String.from_java_bytes(bytes).byteslice(0, count)) if count.positive?
  return :wait_readable unless exception

  raise ::IO::EAGAINWaitReadable, "read would block"
rescue java.io.IOException => e
  raise IOError, e.message
end

#readpartial(length, outbuf = nil) ⇒ String

Blocks until at least one byte is available.

Parameters:

  • length (Integer)
  • outbuf (String, nil) (defaults to: nil)

Returns:

  • (String)


187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/openhab/core/io/transport/serial/serial_port.rb', line 187

def readpartial(length, outbuf = nil)
  check_open
  length = length.to_int
  raise ArgumentError, "negative length" if length.negative?

  result = outbuf.nil? ? +"".b : String.try_convert(outbuf)
  raise TypeError, "no implicit conversion of #{outbuf.class} into String" unless result

  result.replace("".b)
  return result if length.zero?

  bytes = Java::byte[length].new
  loop do
    count = @input.read(bytes, 0, length)
    raise EOFError, "end of file reached" if count.negative?
    return result.replace(String.from_java_bytes(bytes).byteslice(0, count)) if count.positive?

    # Serial receive timeouts can return zero without reaching EOF.
    check_open
    sleep 0.001
  end
rescue java.io.IOException => e
  raise IOError, e.message
end

#wait_readable(timeout = nil) ⇒ self?

Waits for input. A zero timeout only checks availability.

Parameters:

  • timeout (Numeric, nil) (defaults to: nil) —

    Seconds, or nil to wait indefinitely.

Returns:

  • (self, nil)


220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/openhab/core/io/transport/serial/serial_port.rb', line 220

def wait_readable(timeout = nil)
  raise ArgumentError, "negative timeout" if timeout&.negative?

  deadline = monotonic_time + timeout if timeout
  loop do
    check_open
    return self if @input.available.positive?
    return nil if deadline && monotonic_time >= deadline

    sleep(deadline ? (deadline - monotonic_time).clamp(0, 0.01) : 0.01)
  end
rescue java.io.IOException => e
  raise IOError, e.message
end

#write(string, *strings) ⇒ Integer

Writes strings as bytes and flushes them to the port.

Parameters:

  • string (Object)
  • strings (Array<Object>)

Returns:

  • (Integer) —

    Number of bytes written.



105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/openhab/core/io/transport/serial/serial_port.rb', line 105

def write(string, *strings)
  check_open
  bytes_written = [string, *strings].sum do |value|
    bytes = value.to_s.to_java_bytes
    @output.write(bytes, 0, bytes.length)
    bytes.length
  end
  @output.flush
  bytes_written
rescue java.io.IOException => e
  raise IOError, e.message
end