From 0d47f22787669b561415eec1f60d97195190039d Mon Sep 17 00:00:00 2001 From: Federico Fissore Date: Mon, 28 Jan 2013 18:21:41 +0100 Subject: [PATCH] working on #223: Auto-detection of serial ports. Mac version ready even if a bit slow --- app/src/processing/app/Platform.java | 6 +- app/src/processing/app/linux/Platform.java | 21 +-- .../processing/app/linux/UDevAdmParser.java | 16 +++ app/src/processing/app/macosx/Platform.java | 69 +++++++-- .../app/macosx/SystemProfilerParser.java | 62 ++++++++ app/test/processing/app/TestHelper.java | 21 +++ .../app/linux/UDevAdmParserTest.java | 16 +++ app/test/processing/app/linux/udev_output.txt | 24 ++++ .../app/macosx/SystemProfilerParserTest.java | 17 +++ .../app/macosx/system_profiler_output.txt | 134 ++++++++++++++++++ build/macosx/template.app/Contents/Info.plist | 2 +- 11 files changed, 365 insertions(+), 23 deletions(-) create mode 100644 app/src/processing/app/linux/UDevAdmParser.java create mode 100644 app/src/processing/app/macosx/SystemProfilerParser.java create mode 100644 app/test/processing/app/TestHelper.java create mode 100644 app/test/processing/app/linux/UDevAdmParserTest.java create mode 100644 app/test/processing/app/linux/udev_output.txt create mode 100644 app/test/processing/app/macosx/SystemProfilerParserTest.java create mode 100644 app/test/processing/app/macosx/system_profiler_output.txt diff --git a/app/src/processing/app/Platform.java b/app/src/processing/app/Platform.java index 3657acf5a..250efa8e9 100644 --- a/app/src/processing/app/Platform.java +++ b/app/src/processing/app/Platform.java @@ -134,18 +134,18 @@ public class Platform { } } - public String resolveDeviceAttachedTo(String device, Map packages) { + public String resolveDeviceAttachedTo(String serial, Map packages) { return null; } - public String resolveDeviceByVendorIdProductId(Map packages, String vendorId, String productId) { + protected String resolveDeviceByVendorIdProductId(Map packages, String readVIDPID) { for (TargetPackage targetPackage : packages.values()) { for (TargetPlatform targetPlatform : targetPackage.getPlatforms().values()) { for (PreferencesMap board : targetPlatform.getBoards().values()) { if (board.containsKey("vid_pid")) { String[] vidPids = board.get("vid_pid").split(","); for (String vidPid : vidPids) { - if (vidPid.toUpperCase().equals(vendorId + "_" + productId)) { + if (vidPid.toUpperCase().equals(readVIDPID)) { return board.get("name"); } } diff --git a/app/src/processing/app/linux/Platform.java b/app/src/processing/app/linux/Platform.java index 5f4a169f4..b1d5b04ed 100644 --- a/app/src/processing/app/linux/Platform.java +++ b/app/src/processing/app/linux/Platform.java @@ -22,12 +22,6 @@ package processing.app.linux; -import java.io.*; -import java.util.Map; -import java.util.Properties; - -import javax.swing.UIManager; - import org.apache.commons.exec.CommandLine; import org.apache.commons.exec.DefaultExecutor; import org.apache.commons.exec.ExecuteStreamHandler; @@ -36,6 +30,9 @@ import processing.app.Preferences; import processing.app.debug.TargetPackage; import processing.core.PConstants; +import java.io.*; +import java.util.Map; + /** * Used by Base for platform-specific tweaking, for instance finding the @@ -171,11 +168,15 @@ public class Platform extends processing.app.Platform { baos.reset(); CommandLine commandLine = CommandLine.parse("udevadm info --query=property -p " + devicePath); executor.execute(commandLine); - Properties properties = new Properties(); - properties.load(new ByteArrayInputStream(baos.toByteArray())); - return super.resolveDeviceByVendorIdProductId(packages, properties.get("ID_VENDOR_ID").toString().toUpperCase(), properties.get("ID_MODEL_ID").toString().toUpperCase()); + String vidPid = new UDevAdmParser().extractVIDAndPID(new String(baos.toByteArray())); + + if (vidPid == null) { + return super.resolveDeviceAttachedTo(serial, packages); + } + + return super.resolveDeviceByVendorIdProductId(packages, vidPid); } catch (IOException e) { - return null; + return super.resolveDeviceAttachedTo(serial, packages); } } } diff --git a/app/src/processing/app/linux/UDevAdmParser.java b/app/src/processing/app/linux/UDevAdmParser.java new file mode 100644 index 000000000..0b7470fd8 --- /dev/null +++ b/app/src/processing/app/linux/UDevAdmParser.java @@ -0,0 +1,16 @@ +package processing.app.linux; + +import java.io.IOException; +import java.io.StringReader; +import java.util.Properties; + +public class UDevAdmParser { + + public String extractVIDAndPID(String output) throws IOException { + Properties properties = new Properties(); + properties.load(new StringReader(output)); + + return properties.get("ID_VENDOR_ID").toString().toUpperCase() + "_" + properties.get("ID_MODEL_ID").toString().toUpperCase(); + } + +} diff --git a/app/src/processing/app/macosx/Platform.java b/app/src/processing/app/macosx/Platform.java index ee078b31b..35283a217 100644 --- a/app/src/processing/app/macosx/Platform.java +++ b/app/src/processing/app/macosx/Platform.java @@ -22,20 +22,23 @@ package processing.app.macosx; -import java.awt.Insets; -import java.io.File; -import java.io.FileNotFoundException; -import java.lang.reflect.Method; -import java.net.URI; - -import javax.swing.UIManager; - import com.apple.eio.FileManager; - +import org.apache.commons.exec.CommandLine; +import org.apache.commons.exec.DefaultExecutor; +import org.apache.commons.exec.ExecuteStreamHandler; +import org.apache.commons.exec.Executor; import processing.app.Base; +import processing.app.debug.TargetPackage; import processing.core.PApplet; import processing.core.PConstants; +import javax.swing.*; +import java.awt.*; +import java.io.*; +import java.lang.reflect.Method; +import java.net.URI; +import java.util.Map; + /** * Platform handler for Mac OS X. @@ -202,4 +205,52 @@ public class Platform extends processing.app.Platform { return PConstants.platformNames[PConstants.MACOSX]; } + @Override + public String resolveDeviceAttachedTo(String serial, Map packages) { + Executor executor = new DefaultExecutor(); + + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + executor.setStreamHandler(new ExecuteStreamHandler() { + @Override + public void setProcessInputStream(OutputStream outputStream) throws IOException { + } + + @Override + public void setProcessErrorStream(InputStream inputStream) throws IOException { + } + + @Override + public void setProcessOutputStream(InputStream inputStream) throws IOException { + byte[] buf = new byte[4096]; + int bytes = -1; + while ((bytes = inputStream.read(buf)) != -1) { + baos.write(buf, 0, bytes); + } + } + + @Override + public void start() throws IOException { + } + + @Override + public void stop() { + } + }); + + try { + CommandLine toDevicePath = CommandLine.parse("/usr/sbin/system_profiler SPUSBDataType"); + executor.execute(toDevicePath); + String output = new String(baos.toByteArray()); + + String vidPid = new SystemProfilerParser().extractVIDAndPID(output, serial); + + if (vidPid == null) { + return super.resolveDeviceAttachedTo(serial, packages); + } + + return super.resolveDeviceByVendorIdProductId(packages, vidPid); + } catch (IOException e) { + return super.resolveDeviceAttachedTo(serial, packages); + } + } } diff --git a/app/src/processing/app/macosx/SystemProfilerParser.java b/app/src/processing/app/macosx/SystemProfilerParser.java new file mode 100644 index 000000000..da82805b9 --- /dev/null +++ b/app/src/processing/app/macosx/SystemProfilerParser.java @@ -0,0 +1,62 @@ +package processing.app.macosx; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class SystemProfilerParser { + + private final Pattern vidRegex; + private final Pattern serialNumberRegex; + private final Pattern locationRegex; + private final Pattern pidRegex; + + public SystemProfilerParser() { + serialNumberRegex = Pattern.compile("^Serial Number: (.+)$"); + locationRegex = Pattern.compile("^Location ID: (.+)$"); + pidRegex = Pattern.compile("^Product ID: (.+)$"); + vidRegex = Pattern.compile("^Vendor ID: (.+)$"); + } + + public String extractVIDAndPID(String output, String serial) throws IOException { + BufferedReader reader = new BufferedReader(new StringReader(output)); + + String devicePrefix; + if (serial.startsWith("/dev/tty.")) { + devicePrefix = "/dev/tty.usbmodem"; + } else { + devicePrefix = "/dev/cu.usbmodem"; + } + + Map device = new HashMap(); + + String line; + Matcher matcher; + while ((line = reader.readLine()) != null) { + line = line.trim(); + line = line.replaceAll("\\s+", " "); + + if ((matcher = serialNumberRegex.matcher(line)).matches()) { + device.put("serial_number", matcher.group(1)); + } else if ((matcher = locationRegex.matcher(line)).matches()) { + device.put("device_path", devicePrefix + matcher.group(1).substring(2, 6) + "1"); + } else if ((matcher = pidRegex.matcher(line)).matches()) { + device.put("pid", matcher.group(1)); + } else if ((matcher = vidRegex.matcher(line)).matches()) { + device.put("vid", matcher.group(1)); + } else if (line.equals("")) { + if (device.containsKey("serial_number") && device.get("device_path").equals(serial)) { + return device.get("vid").substring(2).toUpperCase() + "_" + device.get("pid").substring(2).toUpperCase(); + } + device = new HashMap(); + } + } + + return null; + } + +} diff --git a/app/test/processing/app/TestHelper.java b/app/test/processing/app/TestHelper.java new file mode 100644 index 000000000..bee55b09e --- /dev/null +++ b/app/test/processing/app/TestHelper.java @@ -0,0 +1,21 @@ +package processing.app; + +import java.io.*; + +public class TestHelper { + + public static String inputStreamToString(InputStream is) throws IOException { + StringWriter sw = new StringWriter(); + + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + String line; + try { + while ((line = reader.readLine()) != null) { + sw.append(line).append('\n'); + } + return sw.toString(); + } finally { + is.close(); + } + } +} diff --git a/app/test/processing/app/linux/UDevAdmParserTest.java b/app/test/processing/app/linux/UDevAdmParserTest.java new file mode 100644 index 000000000..574a01314 --- /dev/null +++ b/app/test/processing/app/linux/UDevAdmParserTest.java @@ -0,0 +1,16 @@ +package processing.app.linux; + +import org.junit.Test; +import processing.app.TestHelper; + +import static org.junit.Assert.assertEquals; + +public class UDevAdmParserTest { + + @Test + public void shouldCorrectlyParse() throws Exception { + String output = TestHelper.inputStreamToString(UDevAdmParserTest.class.getResourceAsStream("udev_output.txt")); + + assertEquals("2341_0036", new UDevAdmParser().extractVIDAndPID(output)); + } +} diff --git a/app/test/processing/app/linux/udev_output.txt b/app/test/processing/app/linux/udev_output.txt new file mode 100644 index 000000000..6d5f4294e --- /dev/null +++ b/app/test/processing/app/linux/udev_output.txt @@ -0,0 +1,24 @@ +DEVLINKS=/dev/arduino_leonardo /dev/serial/by-id/usb-Arduino_LLC_Arduino_Leonardo-if00 /dev/serial/by-path/pci-0000:00:14.0-usb-0:2.3:1.0 +DEVNAME=/dev/ttyACM0 +DEVPATH=/devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2.3/3-2.3:1.0/tty/ttyACM0 +ID_BUS=usb +ID_MM_CANDIDATE=1 +ID_MODEL=Arduino_Leonardo +ID_MODEL_ENC=Arduino\x20Leonardo +ID_MODEL_ID=0036 +ID_PATH=pci-0000:00:14.0-usb-0:2.3:1.0 +ID_PATH_TAG=pci-0000_00_14_0-usb-0_2_3_1_0 +ID_REVISION=0001 +ID_SERIAL=Arduino_LLC_Arduino_Leonardo +ID_TYPE=generic +ID_USB_DRIVER=cdc_acm +ID_USB_INTERFACES=:020201:0a0000: +ID_USB_INTERFACE_NUM=00 +ID_VENDOR=Arduino_LLC +ID_VENDOR_ENC=Arduino\x20LLC +ID_VENDOR_ID=2341 +MAJOR=166 +MINOR=0 +SUBSYSTEM=tty +UDEV_LOG=3 +USEC_INITIALIZED=21035594802 diff --git a/app/test/processing/app/macosx/SystemProfilerParserTest.java b/app/test/processing/app/macosx/SystemProfilerParserTest.java new file mode 100644 index 000000000..46e5edda2 --- /dev/null +++ b/app/test/processing/app/macosx/SystemProfilerParserTest.java @@ -0,0 +1,17 @@ +package processing.app.macosx; + +import org.junit.Test; +import processing.app.TestHelper; + +import static org.junit.Assert.assertEquals; + +public class SystemProfilerParserTest { + + @Test + public void shouldCorrectlyParse() throws Exception { + String output = TestHelper.inputStreamToString(SystemProfilerParserTest.class.getResourceAsStream("system_profiler_output.txt")); + + assertEquals("2341_0044", new SystemProfilerParser().extractVIDAndPID(output, "/dev/cu.usbmodemfa121")); + assertEquals("2341_0044", new SystemProfilerParser().extractVIDAndPID(output, "/dev/tty.usbmodemfa121")); + } +} diff --git a/app/test/processing/app/macosx/system_profiler_output.txt b/app/test/processing/app/macosx/system_profiler_output.txt new file mode 100644 index 000000000..1153ff7db --- /dev/null +++ b/app/test/processing/app/macosx/system_profiler_output.txt @@ -0,0 +1,134 @@ +USB: + + USB High-Speed Bus: + + Host Controller Location: Built-in USB + Host Controller Driver: AppleUSBEHCI + PCI Device ID: 0x1c2d + PCI Revision ID: 0x0005 + PCI Vendor ID: 0x8086 + Bus Number: 0xfa + + Hub: + + Product ID: 0x2513 + Vendor ID: 0x0424 (SMSC) + Version: b.b3 + Speed: Up to 480 Mb/sec + Location ID: 0xfa100000 / 2 + Current Available (mA): 500 + Current Required (mA): 2 + + Trans-It Drive: + + Capacity: 3.99 GB (3,992,977,408 bytes) + Removable Media: Yes + Detachable Drive: Yes + BSD Name: disk1 + Product ID: 0x0622 + Vendor ID: 0x0718 (Imation Corp.) + Version: 1.10 + Serial Number: 0792181A2BDD + Speed: Up to 480 Mb/sec + Manufacturer: TDKMedia + Location ID: 0xfa130000 / 6 + Current Available (mA): 500 + Current Required (mA): 300 + Partition Map Type: MBR (Master Boot Record) + S.M.A.R.T. status: Not Supported + Volumes: + Untitled: + Capacity: 3.99 GB (3,990,379,520 bytes) + Available: 1.99 GB (1,986,838,528 bytes) + Writable: Yes + File System: MS-DOS FAT32 + BSD Name: disk1s1 + Mount Point: /Volumes/Untitled + Content: Windows_FAT_32 + + Arduino Mega ADK: + + Product ID: 0x0044 + Vendor ID: 0x2341 + Version: 0.01 + Serial Number: 64936333936351500000 + Speed: Up to 12 Mb/sec + Manufacturer: Arduino (www.arduino.cc) + Location ID: 0xfa120000 / 5 + Current Available (mA): 500 + Current Required (mA): 100 + + BRCM20702 Hub: + + Product ID: 0x4500 + Vendor ID: 0x0a5c (Broadcom Corp.) + Version: 1.00 + Speed: Up to 12 Mb/sec + Manufacturer: Apple Inc. + Location ID: 0xfa110000 / 3 + Current Available (mA): 500 + Current Required (mA): 94 + + Bluetooth USB Host Controller: + + Product ID: 0x8281 + Vendor ID: 0x05ac (Apple Inc.) + Version: 0.97 + Speed: Up to 12 Mb/sec + Manufacturer: Apple Inc. + Location ID: 0xfa113000 / 4 + Current Available (mA): 500 + Current Required (mA): 0 + + USB High-Speed Bus: + + Host Controller Location: Built-in USB + Host Controller Driver: AppleUSBEHCI + PCI Device ID: 0x7ffc00001c26 + PCI Revision ID: 0x7ffc00000005 + PCI Vendor ID: 0x7ffc00008086 + Bus Number: 0xfd + + Hub: + + Product ID: 0x2513 + Vendor ID: 0x0424 (SMSC) + Version: b.b3 + Speed: Up to 480 Mb/sec + Location ID: 0xfd100000 / 2 + Current Available (mA): 500 + Current Required (mA): 2 + + USB Multimedia Keyboard: + + Product ID: 0xc311 + Vendor ID: 0x046d (Logitech Inc.) + Version: 1.30 + Speed: Up to 1.5 Mb/sec + Manufacturer: BTC + Location ID: 0xfd120000 / 5 + Current Available (mA): 500 + Current Required (mA): 100 + + USB-PS/2 Optical Mouse: + + Product ID: 0xc050 + Vendor ID: 0x046d (Logitech Inc.) + Version: 27.20 + Speed: Up to 1.5 Mb/sec + Manufacturer: Logitech + Location ID: 0xfd130000 / 4 + Current Available (mA): 500 + Current Required (mA): 98 + + IR Receiver: + + Product ID: 0x8242 + Vendor ID: 0x05ac (Apple Inc.) + Version: 0.16 + Speed: Up to 1.5 Mb/sec + Manufacturer: Apple Computer, Inc. + Location ID: 0xfd110000 / 3 + Current Available (mA): 500 + Current Required (mA): 100 + diff --git a/build/macosx/template.app/Contents/Info.plist b/build/macosx/template.app/Contents/Info.plist index 5449b75f7..b1bd96576 100755 --- a/build/macosx/template.app/Contents/Info.plist +++ b/build/macosx/template.app/Contents/Info.plist @@ -76,7 +76,7 @@ - $JAVAROOT/pde.jar:$JAVAROOT/core.jar:$JAVAROOT/antlr.jar:$JAVAROOT/ecj.jar:$JAVAROOT/registry.jar:$JAVAROOT/quaqua.jar:$JAVAROOT/RXTXcomm.jar + $JAVAROOT/pde.jar:$JAVAROOT/core.jar:$JAVAROOT/antlr.jar:$JAVAROOT/ecj.jar:$JAVAROOT/registry.jar:$JAVAROOT/quaqua.jar:$JAVAROOT/RXTXcomm.jar:$JAVAROOT/commons-exec-1.1.jar JVMArchs