lib/rubyserial/windows.rb
# Copyright (c) 2014-2016 The Hybrid Group
class Serial
def initialize(address, baude_rate=9600, data_bits=8, parity=:none, stop_bits=1)
file_opts = RubySerial::Win32::GENERIC_READ | RubySerial::Win32::GENERIC_WRITE
@fd = RubySerial::Win32.CreateFileA("\\\\.\\#{address}", file_opts, 0, nil, RubySerial::Win32::OPEN_EXISTING, 0, nil)
err = FFI.errno
if err != 0
raise RubySerial::Error, RubySerial::Win32::ERROR_CODES[err]
else
@open = true
end
RubySerial::Win32::DCB.new.tap do |dcb|
dcb[:dcblength] = RubySerial::Win32::DCB::Sizeof
err = RubySerial::Win32.GetCommState @fd, dcb
if err == 0
raise RubySerial::Error, RubySerial::Win32::ERROR_CODES[FFI.errno]
end
dcb[:baudrate] = baude_rate
dcb[:bytesize] = data_bits
dcb[:stopbits] = RubySerial::Win32::DCB::STOPBITS[stop_bits]
dcb[:parity] = RubySerial::Win32::DCB::PARITY[parity]
err = RubySerial::Win32.SetCommState @fd, dcb
if err == 0
raise RubySerial::Error, RubySerial::Win32::ERROR_CODES[FFI.errno]
end
end
RubySerial::Win32::CommTimeouts.new.tap do |timeouts|
timeouts[:read_interval_timeout] = 10
timeouts[:read_total_timeout_multiplier] = 1
timeouts[:read_total_timeout_constant] = 10
timeouts[:write_total_timeout_multiplier] = 1
timeouts[:write_total_timeout_constant] = 10
err = RubySerial::Win32.SetCommTimeouts @fd, timeouts
if err == 0
raise RubySerial::Error, RubySerial::Win32::ERROR_CODES[FFI.errno]
end
end
end
def read(size)
buff = FFI::MemoryPointer.new :char, size
count = FFI::MemoryPointer.new :uint32, 1
err = RubySerial::Win32.ReadFile(@fd, buff, size, count, nil)
if err == 0
raise RubySerial::Error, RubySerial::Win32::ERROR_CODES[FFI.errno]
end
buff.get_bytes(0, count.read_int)
end
def getbyte
buff = FFI::MemoryPointer.new :char, 1
count = FFI::MemoryPointer.new :uint32, 1
err = RubySerial::Win32.ReadFile(@fd, buff, 1, count, nil)
if err == 0
raise RubySerial::Error, RubySerial::Win32::ERROR_CODES[FFI.errno]
end
if count.read_int == 0
nil
else
buff.get_bytes(0, 1).bytes.first
end
end
def write(data)
buff = FFI::MemoryPointer.from_string(data.to_s)
count = FFI::MemoryPointer.new :uint32, 1
err = RubySerial::Win32.WriteFile(@fd, buff, buff.size-1, count, nil)
if err == 0
raise RubySerial::Error, RubySerial::Win32::ERROR_CODES[FFI.errno]
end
count.read_int
end
def gets(sep=$/, limit=nil)
if block_given?
loop do
yield(get_until_sep(sep, limit))
end
else
get_until_sep(sep, limit)
end
end
def close
err = RubySerial::Win32.CloseHandle(@fd)
if err == 0
raise RubySerial::Error, RubySerial::Win32::ERROR_CODES[FFI.errno]
else
@open = false
end
end
def closed?
!@open
end
private
def get_until_sep(sep, limit)
sep = "\n\n" if sep == ''
# This allows the method signature to be (sep) or (limit)
(limit = sep; sep="\n") if sep.is_a? Integer
bytes = []
loop do
current_byte = getbyte
bytes << current_byte unless current_byte.nil?
break if (bytes.last(sep.bytes.to_a.size) == sep.bytes.to_a) || ((bytes.size == limit) if limit)
end
bytes.map { |e| e.chr }.join
end
end