Rework Serial ports handling and add Board info menu

This commit introduces the concept of stateful board list (vs. original stateless) and board serial number.

The board is now an "entity" composed by the triplet port/vid/pid. These informations come from libListSerial "light" function. When the board list changes, it triggers a request for the additional infos to libListSerial. These information contains the serial number of the boards.

These brings a lighter and faster scanning process. Some logic has been introduced to handle a board with the S/N only exposed in the bootloader (like 32u4).
In this case the disappearing port acquires the bootloader's S/N

A menu (under Ports menu) shows the currently connected port info and can be used for bugreporting
This commit is contained in:
Martino Facchin 2016-01-25 18:31:27 +01:00
parent c11ceb7dae
commit 243fc68763
11 changed files with 259 additions and 40 deletions

View File

@ -814,6 +814,9 @@ public class Editor extends JFrame implements RunnerListener {
portMenu = new JMenu(tr("Port"));
populatePortMenu();
toolsMenu.add(portMenu);
item = new JMenuItem(tr("Get Board Info"));
item.addActionListener(e -> handleBoardInfo());
toolsMenu.add(item);
toolsMenu.addSeparator();
base.rebuildProgrammerMenu();
@ -2753,6 +2756,52 @@ public class Editor extends JFrame implements RunnerListener {
}).start();
}
private void handleBoardInfo() {
console.clear();
String selectedPort = PreferencesData.get("serial.port");
List<BoardPort> ports = Base.getDiscoveryManager().discovery();
String label = "";
String vid = "";
String pid = "";
String iserial = "";
boolean found = false;
for (BoardPort port : ports) {
if (port.getAddress().equals(selectedPort)) {
label = port.getBoardName();
vid = port.getVID();
pid = port.getPID();
iserial = port.getISerial();
found = true;
break;
}
}
if (!found) {
statusNotice(tr("Please select a port to obtain board info"));
return;
}
if (vid == null || vid.equals("") || vid.equals("0000")) {
statusNotice(tr("Native serial port, can't obtain info"));
return;
}
if (iserial == null || iserial.equals("")) {
iserial = tr("Upload any sketch to obtain it");
}
if (label == null) {
label = tr("Unknown board");
}
String infos = I18n.format("BN: {0}\nVID: {1}\nPID: {2}\nSN: {3}", label, vid, pid, iserial);
JTextArea textArea = new JTextArea(infos);
JOptionPane.showMessageDialog(this, textArea, tr("Board Info"), JOptionPane.PLAIN_MESSAGE);
}
/**
* Handler for File &rarr; Page Setup.

View File

@ -52,6 +52,7 @@ public class EditorLineStatus extends JComponent {
String text = "";
String name = "";
String serialport = "";
String serialnumber = "";
public EditorLineStatus() {
background = Theme.getColor("linestatus.bgcolor");
@ -129,6 +130,10 @@ public class EditorLineStatus extends JComponent {
this.serialport = serialport;
}
public void setSerialNumber(String serialnumber) {
this.serialnumber = serialnumber;
}
public Dimension getPreferredSize() {
return scale(new Dimension(300, height));
}

View File

@ -36,8 +36,12 @@ public class BoardPort {
private String address;
private String protocol;
private String boardName;
private String vid;
private String pid;
private String iserial;
private String label;
private final PreferencesMap prefs;
private boolean online;
public BoardPort() {
this.prefs = new PreferencesMap();
@ -79,4 +83,36 @@ public class BoardPort {
return label;
}
public void setOnlineStatus(boolean online) {
this.online = online;
}
public boolean isOnline() {
return online;
}
public void setVIDPID(String vid, String pid) {
this.vid = vid;
this.pid = pid;
}
public String getVID() {
return vid;
}
public String getPID() {
return pid;
}
public void setISerial(String iserial) {
this.iserial = iserial;
}
public String getISerial() {
return iserial;
}
@Override
public String toString() {
return this.address+"_"+this.vid+"_"+this.pid;
}
}

View File

@ -51,5 +51,6 @@ public interface Discovery {
* @return
*/
List<BoardPort> listDiscoveredBoards();
List<BoardPort> listDiscoveredBoards(boolean complete);
}

View File

@ -40,11 +40,13 @@ import static processing.app.I18n.tr;
public class DiscoveryManager {
private final List<Discovery> discoverers;
private final SerialDiscovery serialDiscoverer = new SerialDiscovery();
private final NetworkDiscovery networkDiscoverer = new NetworkDiscovery();
public DiscoveryManager() {
discoverers = new ArrayList<Discovery>();
discoverers.add(new SerialDiscovery());
discoverers.add(new NetworkDiscovery());
discoverers.add(serialDiscoverer);
discoverers.add(networkDiscoverer);
// Start all discoverers
for (Discovery d : discoverers) {
@ -69,6 +71,10 @@ public class DiscoveryManager {
Runtime.getRuntime().addShutdownHook(closeHook);
}
public SerialDiscovery getSerialDiscoverer() {
return serialDiscoverer;
}
public List<BoardPort> discovery() {
List<BoardPort> res = new ArrayList<BoardPort>();
for (Discovery d : discoverers) {
@ -77,6 +83,14 @@ public class DiscoveryManager {
return res;
}
public List<BoardPort> discovery(boolean complete) {
List<BoardPort> res = new ArrayList<BoardPort>();
for (Discovery d : discoverers) {
res.addAll(d.listDiscoveredBoards(complete));
}
return res;
}
public BoardPort find(String address) {
for (BoardPort boardPort : discovery()) {
if (boardPort.getAddress().equals(address)) {
@ -86,4 +100,13 @@ public class DiscoveryManager {
return null;
}
public BoardPort find(String address, boolean complete) {
for (BoardPort boardPort : discovery(complete)) {
if (boardPort.getAddress().equals(address)) {
return boardPort;
}
}
return null;
}
}

View File

@ -67,6 +67,13 @@ public class NetworkDiscovery implements Discovery, ServiceListener, cc.arduino.
}
}
@Override
public List<BoardPort> listDiscoveredBoards(boolean complete) {
synchronized (reachableBoardPorts) {
return new LinkedList<>(reachableBoardPorts);
}
}
public void setReachableBoardPorts(List<BoardPort> newReachableBoardPorts) {
synchronized (reachableBoardPorts) {
this.reachableBoardPorts.clear();

View File

@ -41,6 +41,7 @@ public class SerialDiscovery implements Discovery {
private Timer serialBoardsListerTimer;
private final List<BoardPort> serialBoardPorts;
private SerialBoardsLister serialBoardsLister = new SerialBoardsLister(this);;
public SerialDiscovery() {
this.serialBoardPorts = new LinkedList<>();
@ -48,26 +49,43 @@ public class SerialDiscovery implements Discovery {
@Override
public List<BoardPort> listDiscoveredBoards() {
return getSerialBoardPorts();
return getSerialBoardPorts(false);
}
private List<BoardPort> getSerialBoardPorts() {
synchronized (serialBoardPorts) {
return new LinkedList<>(serialBoardPorts);
}
public List<BoardPort> listDiscoveredBoards(boolean complete) {
return getSerialBoardPorts(complete);
}
private List<BoardPort> getSerialBoardPorts(boolean complete) {
if (complete) {
return new LinkedList<>(serialBoardPorts);
}
List<BoardPort> onlineBoardPorts = new LinkedList<>();
for (BoardPort port : serialBoardPorts) {
if (port.isOnline() == true) {
onlineBoardPorts.add(port);
}
}
return onlineBoardPorts;
}
public void setSerialBoardPorts(List<BoardPort> newSerialBoardPorts) {
synchronized (serialBoardPorts) {
serialBoardPorts.clear();
serialBoardPorts.addAll(newSerialBoardPorts);
}
}
public void forceRefresh() {
serialBoardsLister.retriggerDiscovery();
}
public void setUploadInProgress(boolean param) {
serialBoardsLister.uploadInProgress = param;
}
@Override
public void start() {
this.serialBoardsListerTimer = new Timer(SerialBoardsLister.class.getName());
new SerialBoardsLister(this).start(serialBoardsListerTimer);
serialBoardsLister.start(serialBoardsListerTimer);
}
@Override

View File

@ -31,9 +31,9 @@ package cc.arduino.packages.discoverers.serial;
import cc.arduino.packages.BoardPort;
import cc.arduino.packages.discoverers.SerialDiscovery;
import cc.arduino.packages.uploaders.SerialUploader;
import processing.app.BaseNoGui;
import processing.app.Platform;
import processing.app.Serial;
import processing.app.debug.TargetBoard;
import java.util.*;
@ -41,6 +41,10 @@ import java.util.*;
public class SerialBoardsLister extends TimerTask {
private final SerialDiscovery serialDiscovery;
private final List<BoardPort> boardPorts = new LinkedList<>();
private List<String> oldPorts = new LinkedList<>();
public boolean uploadInProgress = false;
private BoardPort oldUploadBoardPort = null;
public SerialBoardsLister(SerialDiscovery serialDiscovery) {
this.serialDiscovery = serialDiscovery;
@ -50,8 +54,7 @@ public class SerialBoardsLister extends TimerTask {
timer.schedule(this, 0, 1000);
}
@Override
public void run() {
public void retriggerDiscovery() {
while (BaseNoGui.packages == null) {
try {
Thread.sleep(1000);
@ -59,34 +62,79 @@ public class SerialBoardsLister extends TimerTask {
// noop
}
}
Platform platform = BaseNoGui.getPlatform();
if (platform == null) {
return;
}
List<BoardPort> boardPorts = new LinkedList<>();
List<String> ports = Serial.list();
String devicesListOutput = null;
if (!ports.isEmpty()) {
devicesListOutput = platform.preListAllCandidateDevices();
List<String> ports = platform.listSerials();
if (ports.equals(oldPorts)) {
return;
}
for (String port : ports) {
Map<String, Object> boardData = platform.resolveDeviceByVendorIdProductId(port, BaseNoGui.packages, devicesListOutput);
// if (updating) {}
// a port will disappear, another will appear
// use this information to "merge" the boards
// updating must be signaled by SerialUpload class
BoardPort boardPort = new BoardPort();
oldPorts.clear();
oldPorts.addAll(ports);
for (BoardPort board : boardPorts) {
if (ports.contains(board.toString())) {
if (board.isOnline()) {
ports.remove(ports.indexOf(board.toString()));
}
} else {
if (uploadInProgress && board.isOnline()) {
oldUploadBoardPort = board;
}
board.setOnlineStatus(false);
}
}
for (String newPort : ports) {
String[] parts = newPort.split("_");
String port = parts[0];
Map<String, Object> boardData = platform.resolveDeviceByVendorIdProductId(port, BaseNoGui.packages);
BoardPort boardPort = null;
boolean updatingInfos = false;
int i = 0;
// create new board or update existing
for (BoardPort board : boardPorts) {
if (board.toString().equals(newPort)) {
updatingInfos = true;
boardPort = boardPorts.get(i);
break;
}
i++;
}
if (!updatingInfos) {
boardPort = new BoardPort();
}
boardPort.setAddress(port);
boardPort.setProtocol("serial");
boardPort.setOnlineStatus(true);
String label = port;
if (boardData != null) {
boardPort.getPrefs().put("vid", boardData.get("vid").toString());
boardPort.getPrefs().put("pid", boardData.get("pid").toString());
boardPort.getPrefs().put("iserial", boardData.get("iserial").toString());
boardPort.setVIDPID(parts[1], parts[2]);
String iserial = boardData.get("iserial").toString();
if (iserial.length() >= 10) {
boardPort.getPrefs().put("iserial", iserial);
boardPort.setISerial(iserial);
}
if (uploadInProgress && oldUploadBoardPort!=null) {
oldUploadBoardPort.getPrefs().put("iserial", iserial);
oldUploadBoardPort.setISerial(iserial);
}
TargetBoard board = (TargetBoard) boardData.get("board");
if (board != null) {
@ -96,13 +144,25 @@ public class SerialBoardsLister extends TimerTask {
}
boardPort.setBoardName(boardName);
}
} else {
if (parts[1] != "0000") {
boardPort.setVIDPID(parts[1], parts[2]);
} else {
boardPort.setVIDPID("0000", "0000");
boardPort.setISerial("");
}
}
boardPort.setLabel(label);
boardPorts.add(boardPort);
if (!updatingInfos) {
boardPorts.add(boardPort);
}
}
serialDiscovery.setSerialBoardPorts(boardPorts);
}
@Override
public void run() {
retriggerDiscovery();
}
}

View File

@ -44,6 +44,8 @@ import processing.app.helpers.OSUtils;
import processing.app.helpers.PreferencesMap;
import processing.app.helpers.StringReplacer;
import cc.arduino.packages.discoverers.SerialDiscovery;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
@ -156,13 +158,12 @@ public class SerialUploader extends Uploader {
} else {
prefs.put("serial.port.file", actualUploadPort);
}
}
BoardPort boardPort = BaseNoGui.getDiscoveryManager().find(PreferencesData.get("serial.port"));
try {
prefs.put("serial.port.iserial", boardPort.getPrefs().getOrExcept("iserial"));
} catch (Exception e) {
// if serial port does not contain an iserial field
// retrigger a discovery
BaseNoGui.getDiscoveryManager().getSerialDiscoverer().setUploadInProgress(true);
Thread.sleep(100);
BaseNoGui.getDiscoveryManager().getSerialDiscoverer().forceRefresh();
Thread.sleep(100);
}
prefs.put("build.path", buildPath);
@ -184,6 +185,8 @@ public class SerialUploader extends Uploader {
throw new RunnerException(e);
}
BaseNoGui.getDiscoveryManager().getSerialDiscoverer().setUploadInProgress(false);
String finalUploadPort = null;
if (uploadResult && doTouch) {
try {
@ -196,15 +199,13 @@ public class SerialUploader extends Uploader {
long started = System.currentTimeMillis();
while (System.currentTimeMillis() - started < 2000) {
List<String> portList = Serial.list();
if (portList.contains(actualUploadPort)) {
finalUploadPort = actualUploadPort;
break;
} else if (portList.contains(userSelectedUploadPort)) {
if (portList.contains(userSelectedUploadPort)) {
finalUploadPort = userSelectedUploadPort;
break;
}
Thread.sleep(250);
}
finalUploadPort = actualUploadPort;
}
} catch (InterruptedException ex) {
// noop

View File

@ -33,6 +33,8 @@ import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import cc.arduino.packages.BoardPort;
import static processing.app.I18n.tr;
import static processing.app.helpers.filefilters.OnlyDirs.ONLY_DIRS;
@ -1119,6 +1121,8 @@ public class BaseNoGui {
public static void selectSerialPort(String port) {
PreferencesData.set("serial.port", port);
BoardPort boardPort = getDiscoveryManager().find(port, true);
PreferencesData.set("serial.port.iserial", boardPort.getPrefs().get("iserial"));
String portFile = port;
if (port.startsWith("/dev/")) {
portFile = portFile.substring(5);

View File

@ -35,6 +35,8 @@ import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
import java.util.Arrays;
import static processing.app.I18n.tr;
@ -160,13 +162,26 @@ public class Platform {
}
}
public native String resolveDeviceAttachedToNative(String serial);
private native String resolveDeviceAttachedToNative(String serial);
private native String[] listSerialsNative();
public String preListAllCandidateDevices() {
return null;
}
public Map<String, Object> resolveDeviceByVendorIdProductId(String serial, Map<String, TargetPackage> packages, String devicesListOutput) {
public List<String> listSerials(){
return new ArrayList(Arrays.asList(this.listSerialsNative()));
}
public List<String> listSerialsNames(){
List<String> list = new LinkedList<>();
for (String port : this.listSerialsNative()) {
list.add(port.split("_")[0]);
}
return list;
}
public Map<String, Object> resolveDeviceByVendorIdProductId(String serial, Map<String, TargetPackage> packages) {
String vid_pid_iSerial = resolveDeviceAttachedToNative(serial);
for (TargetPackage targetPackage : packages.values()) {
for (TargetPlatform targetPlatform : targetPackage.getPlatforms().values()) {