Skip to content

Commit 0d47f22

Browse files
author
Federico Fissore
committed
working on #223: Auto-detection of serial ports. Mac version ready even if a bit slow
1 parent 7769527 commit 0d47f22

File tree

11 files changed

+365
-23
lines changed

11 files changed

+365
-23
lines changed

app/src/processing/app/Platform.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -134,18 +134,18 @@ public void openFolder(File file) throws Exception {
134134
}
135135
}
136136

137-
public String resolveDeviceAttachedTo(String device, Map<String, TargetPackage> packages) {
137+
public String resolveDeviceAttachedTo(String serial, Map<String, TargetPackage> packages) {
138138
return null;
139139
}
140140

141-
public String resolveDeviceByVendorIdProductId(Map<String, TargetPackage> packages, String vendorId, String productId) {
141+
protected String resolveDeviceByVendorIdProductId(Map<String, TargetPackage> packages, String readVIDPID) {
142142
for (TargetPackage targetPackage : packages.values()) {
143143
for (TargetPlatform targetPlatform : targetPackage.getPlatforms().values()) {
144144
for (PreferencesMap board : targetPlatform.getBoards().values()) {
145145
if (board.containsKey("vid_pid")) {
146146
String[] vidPids = board.get("vid_pid").split(",");
147147
for (String vidPid : vidPids) {
148-
if (vidPid.toUpperCase().equals(vendorId + "_" + productId)) {
148+
if (vidPid.toUpperCase().equals(readVIDPID)) {
149149
return board.get("name");
150150
}
151151
}

app/src/processing/app/linux/Platform.java

+11-10
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,6 @@
2222

2323
package processing.app.linux;
2424

25-
import java.io.*;
26-
import java.util.Map;
27-
import java.util.Properties;
28-
29-
import javax.swing.UIManager;
30-
3125
import org.apache.commons.exec.CommandLine;
3226
import org.apache.commons.exec.DefaultExecutor;
3327
import org.apache.commons.exec.ExecuteStreamHandler;
@@ -36,6 +30,9 @@
3630
import processing.app.debug.TargetPackage;
3731
import processing.core.PConstants;
3832

33+
import java.io.*;
34+
import java.util.Map;
35+
3936

4037
/**
4138
* Used by Base for platform-specific tweaking, for instance finding the
@@ -171,11 +168,15 @@ public void stop() {
171168
baos.reset();
172169
CommandLine commandLine = CommandLine.parse("udevadm info --query=property -p " + devicePath);
173170
executor.execute(commandLine);
174-
Properties properties = new Properties();
175-
properties.load(new ByteArrayInputStream(baos.toByteArray()));
176-
return super.resolveDeviceByVendorIdProductId(packages, properties.get("ID_VENDOR_ID").toString().toUpperCase(), properties.get("ID_MODEL_ID").toString().toUpperCase());
171+
String vidPid = new UDevAdmParser().extractVIDAndPID(new String(baos.toByteArray()));
172+
173+
if (vidPid == null) {
174+
return super.resolveDeviceAttachedTo(serial, packages);
175+
}
176+
177+
return super.resolveDeviceByVendorIdProductId(packages, vidPid);
177178
} catch (IOException e) {
178-
return null;
179+
return super.resolveDeviceAttachedTo(serial, packages);
179180
}
180181
}
181182
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package processing.app.linux;
2+
3+
import java.io.IOException;
4+
import java.io.StringReader;
5+
import java.util.Properties;
6+
7+
public class UDevAdmParser {
8+
9+
public String extractVIDAndPID(String output) throws IOException {
10+
Properties properties = new Properties();
11+
properties.load(new StringReader(output));
12+
13+
return properties.get("ID_VENDOR_ID").toString().toUpperCase() + "_" + properties.get("ID_MODEL_ID").toString().toUpperCase();
14+
}
15+
16+
}

app/src/processing/app/macosx/Platform.java

+60-9
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,23 @@
2222

2323
package processing.app.macosx;
2424

25-
import java.awt.Insets;
26-
import java.io.File;
27-
import java.io.FileNotFoundException;
28-
import java.lang.reflect.Method;
29-
import java.net.URI;
30-
31-
import javax.swing.UIManager;
32-
3325
import com.apple.eio.FileManager;
34-
26+
import org.apache.commons.exec.CommandLine;
27+
import org.apache.commons.exec.DefaultExecutor;
28+
import org.apache.commons.exec.ExecuteStreamHandler;
29+
import org.apache.commons.exec.Executor;
3530
import processing.app.Base;
31+
import processing.app.debug.TargetPackage;
3632
import processing.core.PApplet;
3733
import processing.core.PConstants;
3834

35+
import javax.swing.*;
36+
import java.awt.*;
37+
import java.io.*;
38+
import java.lang.reflect.Method;
39+
import java.net.URI;
40+
import java.util.Map;
41+
3942

4043
/**
4144
* Platform handler for Mac OS X.
@@ -202,4 +205,52 @@ public String getName() {
202205
return PConstants.platformNames[PConstants.MACOSX];
203206
}
204207

208+
@Override
209+
public String resolveDeviceAttachedTo(String serial, Map<String, TargetPackage> packages) {
210+
Executor executor = new DefaultExecutor();
211+
212+
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
213+
executor.setStreamHandler(new ExecuteStreamHandler() {
214+
@Override
215+
public void setProcessInputStream(OutputStream outputStream) throws IOException {
216+
}
217+
218+
@Override
219+
public void setProcessErrorStream(InputStream inputStream) throws IOException {
220+
}
221+
222+
@Override
223+
public void setProcessOutputStream(InputStream inputStream) throws IOException {
224+
byte[] buf = new byte[4096];
225+
int bytes = -1;
226+
while ((bytes = inputStream.read(buf)) != -1) {
227+
baos.write(buf, 0, bytes);
228+
}
229+
}
230+
231+
@Override
232+
public void start() throws IOException {
233+
}
234+
235+
@Override
236+
public void stop() {
237+
}
238+
});
239+
240+
try {
241+
CommandLine toDevicePath = CommandLine.parse("/usr/sbin/system_profiler SPUSBDataType");
242+
executor.execute(toDevicePath);
243+
String output = new String(baos.toByteArray());
244+
245+
String vidPid = new SystemProfilerParser().extractVIDAndPID(output, serial);
246+
247+
if (vidPid == null) {
248+
return super.resolveDeviceAttachedTo(serial, packages);
249+
}
250+
251+
return super.resolveDeviceByVendorIdProductId(packages, vidPid);
252+
} catch (IOException e) {
253+
return super.resolveDeviceAttachedTo(serial, packages);
254+
}
255+
}
205256
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package processing.app.macosx;
2+
3+
import java.io.BufferedReader;
4+
import java.io.IOException;
5+
import java.io.StringReader;
6+
import java.util.HashMap;
7+
import java.util.Map;
8+
import java.util.regex.Matcher;
9+
import java.util.regex.Pattern;
10+
11+
public class SystemProfilerParser {
12+
13+
private final Pattern vidRegex;
14+
private final Pattern serialNumberRegex;
15+
private final Pattern locationRegex;
16+
private final Pattern pidRegex;
17+
18+
public SystemProfilerParser() {
19+
serialNumberRegex = Pattern.compile("^Serial Number: (.+)$");
20+
locationRegex = Pattern.compile("^Location ID: (.+)$");
21+
pidRegex = Pattern.compile("^Product ID: (.+)$");
22+
vidRegex = Pattern.compile("^Vendor ID: (.+)$");
23+
}
24+
25+
public String extractVIDAndPID(String output, String serial) throws IOException {
26+
BufferedReader reader = new BufferedReader(new StringReader(output));
27+
28+
String devicePrefix;
29+
if (serial.startsWith("/dev/tty.")) {
30+
devicePrefix = "/dev/tty.usbmodem";
31+
} else {
32+
devicePrefix = "/dev/cu.usbmodem";
33+
}
34+
35+
Map<String, String> device = new HashMap<String, String>();
36+
37+
String line;
38+
Matcher matcher;
39+
while ((line = reader.readLine()) != null) {
40+
line = line.trim();
41+
line = line.replaceAll("\\s+", " ");
42+
43+
if ((matcher = serialNumberRegex.matcher(line)).matches()) {
44+
device.put("serial_number", matcher.group(1));
45+
} else if ((matcher = locationRegex.matcher(line)).matches()) {
46+
device.put("device_path", devicePrefix + matcher.group(1).substring(2, 6) + "1");
47+
} else if ((matcher = pidRegex.matcher(line)).matches()) {
48+
device.put("pid", matcher.group(1));
49+
} else if ((matcher = vidRegex.matcher(line)).matches()) {
50+
device.put("vid", matcher.group(1));
51+
} else if (line.equals("")) {
52+
if (device.containsKey("serial_number") && device.get("device_path").equals(serial)) {
53+
return device.get("vid").substring(2).toUpperCase() + "_" + device.get("pid").substring(2).toUpperCase();
54+
}
55+
device = new HashMap<String, String>();
56+
}
57+
}
58+
59+
return null;
60+
}
61+
62+
}
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package processing.app;
2+
3+
import java.io.*;
4+
5+
public class TestHelper {
6+
7+
public static String inputStreamToString(InputStream is) throws IOException {
8+
StringWriter sw = new StringWriter();
9+
10+
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
11+
String line;
12+
try {
13+
while ((line = reader.readLine()) != null) {
14+
sw.append(line).append('\n');
15+
}
16+
return sw.toString();
17+
} finally {
18+
is.close();
19+
}
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package processing.app.linux;
2+
3+
import org.junit.Test;
4+
import processing.app.TestHelper;
5+
6+
import static org.junit.Assert.assertEquals;
7+
8+
public class UDevAdmParserTest {
9+
10+
@Test
11+
public void shouldCorrectlyParse() throws Exception {
12+
String output = TestHelper.inputStreamToString(UDevAdmParserTest.class.getResourceAsStream("udev_output.txt"));
13+
14+
assertEquals("2341_0036", new UDevAdmParser().extractVIDAndPID(output));
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
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
2+
DEVNAME=/dev/ttyACM0
3+
DEVPATH=/devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2.3/3-2.3:1.0/tty/ttyACM0
4+
ID_BUS=usb
5+
ID_MM_CANDIDATE=1
6+
ID_MODEL=Arduino_Leonardo
7+
ID_MODEL_ENC=Arduino\x20Leonardo
8+
ID_MODEL_ID=0036
9+
ID_PATH=pci-0000:00:14.0-usb-0:2.3:1.0
10+
ID_PATH_TAG=pci-0000_00_14_0-usb-0_2_3_1_0
11+
ID_REVISION=0001
12+
ID_SERIAL=Arduino_LLC_Arduino_Leonardo
13+
ID_TYPE=generic
14+
ID_USB_DRIVER=cdc_acm
15+
ID_USB_INTERFACES=:020201:0a0000:
16+
ID_USB_INTERFACE_NUM=00
17+
ID_VENDOR=Arduino_LLC
18+
ID_VENDOR_ENC=Arduino\x20LLC
19+
ID_VENDOR_ID=2341
20+
MAJOR=166
21+
MINOR=0
22+
SUBSYSTEM=tty
23+
UDEV_LOG=3
24+
USEC_INITIALIZED=21035594802
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package processing.app.macosx;
2+
3+
import org.junit.Test;
4+
import processing.app.TestHelper;
5+
6+
import static org.junit.Assert.assertEquals;
7+
8+
public class SystemProfilerParserTest {
9+
10+
@Test
11+
public void shouldCorrectlyParse() throws Exception {
12+
String output = TestHelper.inputStreamToString(SystemProfilerParserTest.class.getResourceAsStream("system_profiler_output.txt"));
13+
14+
assertEquals("2341_0044", new SystemProfilerParser().extractVIDAndPID(output, "/dev/cu.usbmodemfa121"));
15+
assertEquals("2341_0044", new SystemProfilerParser().extractVIDAndPID(output, "/dev/tty.usbmodemfa121"));
16+
}
17+
}

0 commit comments

Comments
 (0)