Class: OpenC3::PosixSerialDriver

Inherits:
Object
  • Object
show all
Defined in:
lib/openc3/io/posix_serial_driver.rb

Overview

Serial driver for use on Posix serial ports found on UNIX based systems

Instance Method Summary collapse

Constructor Details

#initialize(port_name = '/dev/ttyS0', baud_rate = 9600, parity = :NONE, stop_bits = 1, write_timeout = 10.0, read_timeout = nil, flow_control = :NONE, data_bits = 8) ⇒ PosixSerialDriver

Returns a new instance of PosixSerialDriver.

Parameters:

  • port_name (String) (defaults to: '/dev/ttyS0')

    Name of the serial port

  • baud_rate (Integer) (defaults to: 9600)

    Serial port baud rate

  • parity (Symbol) (defaults to: :NONE)

    Must be one of :EVEN, :ODD or :NONE

  • stop_bits (Integer) (defaults to: 1)

    Number of stop bits

  • write_timeout (Float) (defaults to: 10.0)

    Seconds to wait before aborting writes

  • read_timeout (Float|nil) (defaults to: nil)

    Seconds to wait before aborting reads. Pass nil to block until the read is complete.

  • flow_control (Symbol) (defaults to: :NONE)

    Currently supported :NONE and :RTSCTS (default :NONE)

  • data_bits (Integer) (defaults to: 8)

    Number of data bits (default 8)

Raises:

  • (ArgumentError)


31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/openc3/io/posix_serial_driver.rb', line 31

def initialize(port_name = '/dev/ttyS0',
               baud_rate = 9600,
               parity = :NONE,
               stop_bits = 1,
               write_timeout = 10.0,
               read_timeout = nil,
               flow_control = :NONE,
               data_bits = 8)

  # Convert Baud Rate into Termios constant
  begin
    baud_rate = Object.const_get("Termios::B#{baud_rate}")
  rescue NameError
    raise(ArgumentError, "Invalid Baud Rate, Not Defined by Termios: #{baud_rate}")
  end

  # Verify Parameters
  raise(ArgumentError, "Invalid Data Bits: #{data_bits}") unless [5, 6, 7, 8].include?(data_bits)
  raise(ArgumentError, "Invalid parity: #{parity}") if parity and !SerialDriver::VALID_PARITY.include?(parity)
  raise(ArgumentError, "Invalid Stop Bits: #{stop_bits}") unless [1, 2].include?(stop_bits)

  @write_timeout = write_timeout
  @read_timeout = read_timeout

  parity = nil if parity == :NONE

  # Open the serial Port
  @handle = Kernel.open(port_name, File::RDWR | File::NONBLOCK)
  flags = @handle.fcntl(Fcntl::F_GETFL, 0)
  @handle.fcntl(Fcntl::F_SETFL, flags & ~File::NONBLOCK)
  @handle.extend Termios

  # Configure the serial Port
  tio = Termios.new_termios()
  iflags = 0
  iflags |= Termios::IGNPAR unless parity
  cflags = 0
  cflags |= Termios::CREAD # Enable receiver
  cflags |= Termios.const_get("CS#{data_bits}") # data bits
  cflags |= Termios::CLOCAL # Ignore Modem Control Lines
  cflags |= Termios::CSTOPB if stop_bits == 2
  cflags |= Termios::PARENB if parity
  cflags |= Termios::PARODD if parity == :ODD
  cflags |= Termios::CRTSCTS if flow_control == :RTSCTS
  tio.iflag = iflags
  tio.oflag = 0
  tio.cflag = cflags
  tio.lflag = 0
  tio.cc[Termios::VTIME] = 0
  tio.cc[Termios::VMIN] = 1
  tio.ispeed = baud_rate
  tio.ospeed = baud_rate
  @handle.tcflush(Termios::TCIOFLUSH)
  @handle.tcsetattr(Termios::TCSANOW, tio)

  @pipe_reader, @pipe_writer = IO.pipe
  @readers = [@handle, @pipe_reader]
end

Instance Method Details

#closeObject

Disconnects the driver from the comm port



91
92
93
94
95
96
97
98
99
# File 'lib/openc3/io/posix_serial_driver.rb', line 91

def close
  if @handle
    # Close the serial Port
    @pipe_writer.write('.')
    @pipe_writer.close
    @handle.close
    @handle = nil
  end
end

#closed?Boolean

Returns Whether the serial port has been closed.

Returns:

  • (Boolean)

    Whether the serial port has been closed



102
103
104
105
106
107
108
# File 'lib/openc3/io/posix_serial_driver.rb', line 102

def closed?
  if @handle
    false
  else
    true
  end
end

#readString

Returns Binary data read from the serial port.

Returns:

  • (String)

    Binary data read from the serial port



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/openc3/io/posix_serial_driver.rb', line 136

def read
  begin
    data = @handle.read_nonblock(65535)
  rescue Errno::EAGAIN, Errno::EWOULDBLOCK
    begin
      read_ready, _ = IO.fast_select(@readers, nil, nil, @read_timeout)
    rescue IOError
      @pipe_reader.close unless @pipe_reader.closed?
      return ""
    end
    if read_ready
      if read_ready.include?(@pipe_reader)
        @pipe_reader.close unless @pipe_reader.closed?
        return ""
      else
        retry
      end
    else
      raise Timeout::Error, "Read Timeout"
    end
  end

  data
end

#read_nonblockString

Returns Binary data read from the serial port.

Returns:

  • (String)

    Binary data read from the serial port



162
163
164
165
166
167
168
169
170
171
172
# File 'lib/openc3/io/posix_serial_driver.rb', line 162

def read_nonblock
  data = ''

  begin
    data = @handle.read_nonblock(65535)
  rescue Errno::EAGAIN, Errno::EWOULDBLOCK
    # Do Nothing
  end

  data
end

#write(data) ⇒ Object

Parameters:

  • data (String)

    Binary data to write to the serial port



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/openc3/io/posix_serial_driver.rb', line 111

def write(data)
  num_bytes_to_send = data.length
  total_bytes_sent = 0
  bytes_sent = 0
  data_to_send = data

  loop do
    begin
      bytes_sent = @handle.write_nonblock(data_to_send)
    rescue Errno::EAGAIN, Errno::EWOULDBLOCK
      result = IO.fast_select(nil, [@handle], nil, @write_timeout)
      if result
        retry
      else
        raise Timeout::Error, "Write Timeout"
      end
    end
    total_bytes_sent += bytes_sent
    break if total_bytes_sent >= num_bytes_to_send

    data_to_send = data[total_bytes_sent..-1]
  end
end