Skip to content

Commit

Permalink
✨Add support for controlling multiple connected devices, mirroring ea…
Browse files Browse the repository at this point in the history
…ch command to each device
  • Loading branch information
eirvandelden committed Mar 8, 2024
1 parent 2dbc14d commit 898e57c
Showing 1 changed file with 68 additions and 64 deletions.
132 changes: 68 additions & 64 deletions libcombustd/communication/ambx.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,100 +12,104 @@
class Ambx
include Singleton

@device = nil # device in the usb tree
@handle = nil # device opened
@bus = nil # the bus that the device is on
@device = nil # device in the usb tree
@handle = nil # device opened
@devices = [ ]

# Find the device by finding it in the device tree, fail if it's not connected
def self.connect
LIBUSB::Context.new.devices.each do |dev|
if dev.idVendor == ProtocolDefinitions::USB_VENDOR_ID && dev.idProduct == ProtocolDefinitions::USB_PRODUCT_ID
LIBUSB::Context.new.devices.select do |dev|
dev.idVendor == ProtocolDefinitions::USB_VENDOR_ID && dev.idProduct == ProtocolDefinitions::USB_PRODUCT_ID
end.each do |dev|
if !@device.nil?
@device = dev
@bus = dev.bus_number
break
# break
end
end

if @device.nil?
@handle = nil
@bus = nil
return false
end
@devices << dev

true
true
end
end

# Open the device if it has been connected before.
# If the device hasn't been opened yet, try to open it otherwise fail
def self.open
return false if @device.nil? && !Ambx.connect
return false if @devices.all? { |dev| dev.nil? } && !Ambx.connect

@handle = @device.open
@handles = @devices.map { |device| device.open }
# we retry a few times to open the device or kill it
unless @handle.nil?
retries = 0
begin
begin
error_code = @handle.claim_interface(0)
rescue ArgumentError
end

raise CannotClaimInterfaceError if error_code.nil? # TODO: libusb doesn't return anything on error
return true
rescue CannotClaimInterfaceError
@handle.auto_detach_kernel_driver = true
retries += 1
retry
else
false
end
if @handles.none? { |handle| handle.nil? }
@handles.each { |handle| Ambx.claim_interface(handle) }
end
end

# Try to claim interface
def self.claim_interface(handle)
retries = 0
begin
error_code = handle.claim_interface(0)
rescue ArgumentError
end

raise CannotClaimInterfaceError if error_code.nil? # TODO: libusb doesn't return anything on error
return true
rescue CannotClaimInterfaceError
handle.auto_detach_kernel_driver = true
retries += 1
retry
else
false
end

# Close the device if it is open.
# set clearLights to true to try and set the lights back to 0x00
def self.close (clearLights = false)
unless @handle.nil?
if clearLights
Ambx.write([0xA1, Lights::LEFT, ProtocolDefinitions::SET_LIGHT_COLOR, 0x00, 0x00, 0x00])
Ambx.write([0xA1, Lights::WWLEFT, ProtocolDefinitions::SET_LIGHT_COLOR, 0x00, 0x00, 0x00])
Ambx.write([0xA1, Lights::WWCENTER, ProtocolDefinitions::SET_LIGHT_COLOR, 0x00, 0x00, 0x00])
Ambx.write([0xA1, Lights::WWRIGHT, ProtocolDefinitions::SET_LIGHT_COLOR, 0x00, 0x00, 0x00])
Ambx.write([0xA1, Lights::RIGHT, ProtocolDefinitions::SET_LIGHT_COLOR, 0x00, 0x00, 0x00])
end
return if @handles.all? { |handle| handle.nil? }

begin
@handle.close
rescue Errno::ENXIO
end
@handles.each { |handle| Ambx.close_device(handle, clearLights) }

@device = nil
@handles = nil
@devices = nil
end

@handle = nil
@device = nil
def self.close_device(handle, clearLights = false)
if clearLights
Ambx.write([0xA1, Lights::LEFT, ProtocolDefinitions::SET_LIGHT_COLOR, 0x00, 0x00, 0x00])
Ambx.write([0xA1, Lights::WWLEFT, ProtocolDefinitions::SET_LIGHT_COLOR, 0x00, 0x00, 0x00])
Ambx.write([0xA1, Lights::WWCENTER, ProtocolDefinitions::SET_LIGHT_COLOR, 0x00, 0x00, 0x00])
Ambx.write([0xA1, Lights::WWRIGHT, ProtocolDefinitions::SET_LIGHT_COLOR, 0x00, 0x00, 0x00])
Ambx.write([0xA1, Lights::RIGHT, ProtocolDefinitions::SET_LIGHT_COLOR, 0x00, 0x00, 0x00])
end

unless @device.nil?
@device = nil
begin
handle.close
rescue Errno::ENXIO
end
end

# Write a set of bytes to the usb device, this is our command string. Try to open it if necessarily.
def self.write(bytes)
unless @handle || @device
# we lost it. see issue #1 on google code.
end
return if @handles.all? { |handle| handle.nil? } # we lost it. see issue #1 on google code.

unless @handle.nil? && @device.nil?
begin
@handle.interrupt_transfer({
endpoint: ProtocolDefinitions::ENDPOINT_OUT,
dataOut: bytes.pack("C*"),
timeout: 0
})
# quick fix to not immediately segfault, but wait for segfault when application quits.
# need a fix somewhere in ruby_usb, see issue #1 on google code.
rescue Errno::ENXIO
Ambx.close
end
@handles.each do |handle|
next if handle.nil?

Ambx.write_device(handle, bytes)
end
end

# Write a set of bytes to the usb device, this is our command string. Try to open it if necessarily.
def self.write_device(handle, bytes)
handle.interrupt_transfer({
endpoint: ProtocolDefinitions::ENDPOINT_OUT,
dataOut: bytes.pack("C*"),
timeout: 0
})
# quick fix to not immediately segfault, but wait for segfault when application quits.
# need a fix somewhere in ruby_usb, see issue #1 on google code.
rescue Errno::ENXIO
Ambx.close
end
end

0 comments on commit 898e57c

Please sign in to comment.