1
0
mirror of https://github.com/arduino/Arduino.git synced 2025-03-13 10:29:35 +01:00

Merge pull request #8038 from cmaglie/pluggable-discovery

Pluggable discovery: search in platform.txt (WIP)
This commit is contained in:
Martino Facchin 2019-03-07 14:13:43 +01:00 committed by GitHub
commit 0e45f4e0d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 631 additions and 232 deletions

View File

@ -267,6 +267,8 @@ public class Base {
splash.splashText(tr("Initializing packages..."));
BaseNoGui.initPackages();
parser.getUploadPort().ifPresent(BaseNoGui::selectSerialPort);
splash.splashText(tr("Preparing boards..."));
if (!isCommandLine()) {

View File

@ -52,7 +52,6 @@ import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedList;
@ -96,6 +95,7 @@ import cc.arduino.view.StubMenuListener;
import cc.arduino.view.findreplace.FindReplace;
import jssc.SerialPortException;
import processing.app.debug.RunnerException;
import processing.app.debug.TargetBoard;
import processing.app.forms.PasswordAuthorizationDialog;
import processing.app.helpers.DocumentTextChangeListener;
import processing.app.helpers.Keys;
@ -149,9 +149,6 @@ public class Editor extends JFrame implements RunnerListener {
}
}
private final static List<String> BOARD_PROTOCOLS_ORDER = Arrays.asList("serial", "network");
private final static List<String> BOARD_PROTOCOLS_ORDER_TRANSLATIONS = Arrays.asList(tr("Serial ports"), tr("Network ports"));
final Base base;
// otherwise, if the window is resized with the message label
@ -1043,22 +1040,30 @@ public class Editor extends JFrame implements RunnerListener {
private BoardPort port;
public BoardPortJCheckBoxMenuItem(BoardPort port) {
super(port.getLabel());
super();
this.port = port;
setText(toString());
addActionListener(e -> {
selectSerialPort(port.getAddress());
base.onBoardOrPortChange();
});
this.port = port;
}
@Override
public String toString() {
// This is required for serialPrompt()
return port.getLabel();
String label = port.getLabel();
if (port.getBoardName() != null && !port.getBoardName().isEmpty()) {
label += " (" + port.getBoardName() + ")";
}
return label;
}
}
private void populatePortMenu() {
final List<String> PROTOCOLS_ORDER = Arrays.asList("serial", "network");
final List<String> PROTOCOLS_LABELS = Arrays.asList(tr("Serial ports"), tr("Network ports"));
portMenu.removeAll();
String selectedPort = PreferencesData.get("serial.port");
@ -1067,31 +1072,43 @@ public class Editor extends JFrame implements RunnerListener {
ports = platform.filterPorts(ports, PreferencesData.getBoolean("serial.ports.showall"));
Collections.sort(ports, new Comparator<BoardPort>() {
@Override
public int compare(BoardPort o1, BoardPort o2) {
return (BOARD_PROTOCOLS_ORDER.indexOf(o1.getProtocol()) - BOARD_PROTOCOLS_ORDER.indexOf(o2.getProtocol())) * 10 +
o1.getAddress().compareTo(o2.getAddress());
}
ports.stream() //
.filter(port -> port.getProtocolLabel() == null || port.getProtocolLabel().isEmpty())
.forEach(port -> {
int labelIdx = PROTOCOLS_ORDER.indexOf(port.getProtocol());
if (labelIdx != -1) {
port.setProtocolLabel(PROTOCOLS_LABELS.get(labelIdx));
} else {
port.setProtocolLabel(port.getProtocol());
}
});
Collections.sort(ports, (port1, port2) -> {
String pr1 = port1.getProtocol();
String pr2 = port2.getProtocol();
int prIdx1 = PROTOCOLS_ORDER.contains(pr1) ? PROTOCOLS_ORDER.indexOf(pr1) : 999;
int prIdx2 = PROTOCOLS_ORDER.contains(pr2) ? PROTOCOLS_ORDER.indexOf(pr2) : 999;
int r = prIdx1 - prIdx2;
if (r != 0)
return r;
r = port1.getProtocolLabel().compareTo(port2.getProtocolLabel());
if (r != 0)
return r;
return port1.getAddress().compareTo(port2.getAddress());
});
String lastProtocol = null;
String lastProtocolTranslated;
String lastProtocol = "";
String lastProtocolLabel = "";
for (BoardPort port : ports) {
if (lastProtocol == null || !port.getProtocol().equals(lastProtocol)) {
if (lastProtocol != null) {
if (!port.getProtocol().equals(lastProtocol) || !port.getProtocolLabel().equals(lastProtocolLabel)) {
if (!lastProtocol.isEmpty()) {
portMenu.addSeparator();
}
lastProtocol = port.getProtocol();
if (BOARD_PROTOCOLS_ORDER.indexOf(port.getProtocol()) != -1) {
lastProtocolTranslated = BOARD_PROTOCOLS_ORDER_TRANSLATIONS.get(BOARD_PROTOCOLS_ORDER.indexOf(port.getProtocol()));
} else {
lastProtocolTranslated = port.getProtocol();
}
JMenuItem lastProtocolMenuItem = new JMenuItem(tr(lastProtocolTranslated));
lastProtocolMenuItem.setEnabled(false);
portMenu.add(lastProtocolMenuItem);
lastProtocolLabel = port.getProtocolLabel();
JMenuItem item = new JMenuItem(tr(lastProtocolLabel));
item.setEnabled(false);
portMenu.add(item);
}
String address = port.getAddress();
@ -2403,9 +2420,9 @@ public class Editor extends JFrame implements RunnerListener {
for (BoardPort port : ports) {
if (port.getAddress().equals(selectedPort)) {
label = port.getBoardName();
vid = port.getVID();
pid = port.getPID();
iserial = port.getISerial();
vid = port.getPrefs().get("vid");
pid = port.getPrefs().get("pid");
iserial = port.getPrefs().get("iserial");
protocol = port.getProtocol();
found = true;
break;
@ -2575,12 +2592,12 @@ public class Editor extends JFrame implements RunnerListener {
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
protected void onBoardOrPortChange() {
Map<String, String> boardPreferences = BaseNoGui.getBoardPreferences();
if (boardPreferences != null)
lineStatus.setBoardName(boardPreferences.get("name"));
TargetBoard board = BaseNoGui.getTargetBoard();
if (board != null)
lineStatus.setBoardName(board.getName());
else
lineStatus.setBoardName("-");
lineStatus.setSerialPort(PreferencesData.get("serial.port"));
lineStatus.setPort(PreferencesData.get("serial.port"));
lineStatus.repaint();
}

View File

@ -51,8 +51,7 @@ public class EditorLineStatus extends JComponent {
String text = "";
String name = "";
String serialport = "";
String serialnumber = "";
String port = "";
public EditorLineStatus() {
background = Theme.getColor("linestatus.bgcolor");
@ -92,13 +91,13 @@ public class EditorLineStatus extends JComponent {
public void paintComponent(Graphics graphics) {
Graphics2D g = Theme.setupGraphics2D(graphics);
if (name.isEmpty() && serialport.isEmpty()) {
if (name.isEmpty() && port.isEmpty()) {
PreferencesMap boardPreferences = BaseNoGui.getBoardPreferences();
if (boardPreferences != null)
setBoardName(boardPreferences.get("name"));
else
setBoardName("-");
setSerialPort(PreferencesData.get("serial.port"));
setPort(PreferencesData.get("serial.port"));
}
g.setColor(background);
Dimension size = getSize();
@ -112,8 +111,8 @@ public class EditorLineStatus extends JComponent {
g.setColor(messageForeground);
String statusText;
if (serialport != null && !serialport.isEmpty()) {
statusText = I18n.format(tr("{0} on {1}"), name, serialport);
if (port != null && !port.isEmpty()) {
statusText = I18n.format(tr("{0} on {1}"), name, port);
} else {
statusText = name;
}
@ -132,12 +131,8 @@ public class EditorLineStatus extends JComponent {
this.name = name;
}
public void setSerialPort(String serialport) {
this.serialport = serialport;
}
public void setSerialNumber(String serialnumber) {
this.serialnumber = serialnumber;
public void setPort(String port) {
this.port = port;
}
public Dimension getPreferredSize() {

View File

@ -29,22 +29,36 @@
package cc.arduino.packages;
import processing.app.BaseNoGui;
import processing.app.debug.TargetBoard;
import processing.app.debug.TargetPackage;
import processing.app.debug.TargetPlatform;
import processing.app.helpers.PreferencesMap;
public class BoardPort {
private String address;
private String protocol;
private String address; // unique name for this port, used by Preferences
private String protocol; // how to communicate, used for Ports menu sections
private String protocolLabel; // protocol extended name to display on GUI
private String boardName;
private String vid;
private String pid;
private String iserial;
private String label;
private final PreferencesMap prefs;
private boolean online;
private String label; // friendly name shown in Ports menu
private final PreferencesMap identificationPrefs; // data to match with boards.txt
private final PreferencesMap prefs; // "vendorId", "productId", "serialNumber"
private boolean online; // used by SerialBoardsLister (during upload??)
public BoardPort() {
this.prefs = new PreferencesMap();
this.identificationPrefs = new PreferencesMap();
}
public BoardPort(BoardPort bp) {
prefs = new PreferencesMap(bp.prefs);
identificationPrefs = new PreferencesMap(bp.identificationPrefs);
address = bp.address;
protocol = bp.protocol;
boardName = bp.boardName;
label = bp.label;
online = bp.online;
}
public String getAddress() {
@ -63,6 +77,14 @@ public class BoardPort {
this.protocol = protocol;
}
public String getProtocolLabel() {
return protocolLabel;
}
public void setProtocolLabel(String protocolLabel) {
this.protocolLabel = protocolLabel;
}
public String getBoardName() {
return boardName;
}
@ -75,6 +97,10 @@ public class BoardPort {
return prefs;
}
public PreferencesMap getIdentificationPrefs() {
return identificationPrefs;
}
public void setLabel(String label) {
this.label = label;
}
@ -91,28 +117,76 @@ public class BoardPort {
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;
return this.address;
}
// Search for the board which matches identificationPrefs.
// If found, boardName is set to the name from boards.txt
// and the board is returned. If not found, null is returned.
public TargetBoard searchMatchingBoard() {
if (identificationPrefs == null || identificationPrefs.isEmpty()) return null;
for (TargetPackage targetPackage : BaseNoGui.packages.values()) {
for (TargetPlatform targetPlatform : targetPackage.getPlatforms().values()) {
for (TargetBoard board : targetPlatform.getBoards().values()) {
if (matchesBoard(board)) {
setBoardName(board.getName());
return board;
}
}
}
}
return null;
}
public boolean matchesBoard(TargetBoard board) {
PreferencesMap identificationProps = getIdentificationPrefs();
PreferencesMap boardProps = board.getPreferences();
String wildMatcher = identificationProps.get(".");
if (wildMatcher != null) {
if (wildMatcher.equals(board.getId())) {
return true;
}
if (wildMatcher.equals(board.getFQBN())) {
return true;
}
}
// Identification properties are defined in boards.txt with a ".N" suffix
// for example:
//
// uno.name=Arduino/Genuino Uno
// uno.vid.0=0x2341
// uno.pid.0=0x0043
// uno.vid.1=0x2341
// uno.pid.1=0x0001
// uno.vid.2=0x2A03
// uno.pid.2=0x0043
// uno.vid.3=0x2341
// uno.pid.3=0x0243
//
// so we must search starting from suffix ".0" and increasing until we
// found a match or the board has no more identification properties defined
for (int suffix = 0;; suffix++) {
boolean found = true;
for (String prop : identificationProps.keySet()) {
String value = identificationProps.get(prop);
prop += "." + suffix;
if (!boardProps.containsKey(prop)) {
return false;
}
if (!value.equalsIgnoreCase(boardProps.get(prop))) {
found = false;
break;
}
}
if (found) {
return true;
}
}
}
}

View File

@ -29,13 +29,21 @@
package cc.arduino.packages;
import cc.arduino.packages.discoverers.NetworkDiscovery;
import cc.arduino.packages.discoverers.SerialDiscovery;
import static processing.app.I18n.format;
import static processing.app.I18n.tr;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static processing.app.I18n.tr;
import cc.arduino.packages.discoverers.PluggableDiscovery;
import cc.arduino.packages.discoverers.serial.SerialDiscovery;
import cc.arduino.packages.discoverers.NetworkDiscovery;
import processing.app.PreferencesData;
import processing.app.debug.TargetPackage;
import processing.app.debug.TargetPlatform;
import processing.app.helpers.PreferencesMap;
import processing.app.helpers.StringReplacer;
public class DiscoveryManager {
@ -43,17 +51,47 @@ public class DiscoveryManager {
private final SerialDiscovery serialDiscoverer = new SerialDiscovery();
private final NetworkDiscovery networkDiscoverer = new NetworkDiscovery();
public DiscoveryManager() {
// private final Map<String, TargetPackage> packages;
public DiscoveryManager(Map<String, TargetPackage> packages) {
// this.packages = packages;
discoverers = new ArrayList<>();
discoverers.add(serialDiscoverer);
discoverers.add(networkDiscoverer);
// Search for discoveries in installed packages
for (TargetPackage targetPackage : packages.values()) {
for (TargetPlatform platform: targetPackage.getPlatforms().values()) {
//System.out.println("installed: "+platform);
PreferencesMap prefs = platform.getPreferences().subTree("discovery");
for (String discoveryName : prefs.firstLevelMap().keySet()) {
PreferencesMap discoveryPrefs = prefs.subTree(discoveryName);
String pattern = discoveryPrefs.get("pattern");
if (pattern == null) {
System.out.println(format(tr("No recipes defined for discovery '{0}'"),discoveryName));
continue;
}
try {
System.out.println("found discovery: " + discoveryName + " -> " + pattern);
System.out.println("with preferencess -> " + discoveryPrefs);
pattern = StringReplacer.replaceFromMapping(pattern, PreferencesData.getMap());
String[] cmd = StringReplacer.formatAndSplit(pattern, discoveryPrefs);
discoverers.add(new PluggableDiscovery(discoveryName, cmd));
} catch (Exception e) {
System.out.println(format(tr("Could not start discovery '{0}': {1}"), discoveryName, e.getMessage()));
}
}
}
}
// Start all discoverers
for (Discovery d : discoverers) {
try {
new Thread(d).start();
} catch (Exception e) {
System.err.println(tr("Error starting discovery method: ") + d.getClass());
System.err.println(tr("Error starting discovery method: ") + d.toString());
e.printStackTrace();
}
}

View File

@ -113,15 +113,12 @@ public class NetworkDiscovery implements Discovery, ServiceListener, Runnable {
String label = name + " at " + address;
if (board != null && BaseNoGui.packages != null) {
String boardName = BaseNoGui.getPlatform().resolveDeviceByBoardID(BaseNoGui.packages, board);
if (boardName != null) {
label += " (" + boardName + ")";
}
port.setBoardName(boardName);
} else if (description != null) {
label += " (" + description + ")";
}
port.setAddress(address);
port.setBoardName(name);
port.setProtocol("network");
port.setLabel(label);
@ -165,7 +162,7 @@ public class NetworkDiscovery implements Discovery, ServiceListener, Runnable {
@Override
public List<BoardPort> listDiscoveredBoards() {
synchronized (reachableBoardPorts) {
synchronized (reachableBoardPorts) {
return getBoardPortsDiscoveredWithJmDNS();
}
}
@ -179,8 +176,8 @@ public class NetworkDiscovery implements Discovery, ServiceListener, Runnable {
public void setReachableBoardPorts(List<BoardPort> newReachableBoardPorts) {
synchronized (reachableBoardPorts) {
this.reachableBoardPorts.clear();
this.reachableBoardPorts.addAll(newReachableBoardPorts);
reachableBoardPorts.clear();
reachableBoardPorts.addAll(newReachableBoardPorts);
}
}

View File

@ -0,0 +1,292 @@
/*
* This file is part of Arduino.
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2018 Arduino SA (http://www.arduino.cc/)
*/
package cc.arduino.packages.discoverers;
import static processing.app.I18n.format;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import cc.arduino.packages.BoardPort;
import cc.arduino.packages.Discovery;
import processing.app.PreferencesData;
import processing.app.helpers.StringUtils;
public class PluggableDiscovery implements Discovery {
private final String discoveryName;
private final String[] cmd;
private final List<BoardPort> portList = new ArrayList<>();
private Process program=null;
private Thread pollingThread;
private void debug(String x) {
if (PreferencesData.getBoolean("discovery.debug"))
System.out.println(discoveryName + ": " + x);
}
public PluggableDiscovery(String discoveryName, String[] cmd) {
this.cmd = cmd;
this.discoveryName = discoveryName;
}
@Override
public void run() {
// this method is started as a new thread, it will constantly listen
// to the discovery tool and keep track of the discovered ports
try {
start();
InputStream input = program.getInputStream();
JsonFactory factory = new JsonFactory();
JsonParser parser = factory.createParser(input);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, Visibility.NONE);
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
while (program != null && program.isAlive()) {
JsonNode tree = mapper.readTree(parser);
if (tree == null) {
if (program != null && program.isAlive()) {
System.err.println(format("{0}: Invalid json message", discoveryName));
}
break;
}
debug("Received json: " + tree);
processJsonNode(mapper, tree);
}
debug("thread exit normally");
} catch (InterruptedException e) {
debug("thread exit by interrupt");
e.printStackTrace();
} catch (Exception e) {
debug("thread exit other exception");
e.printStackTrace();
}
try {
stop();
} catch (Exception e) {
}
}
private void processJsonNode(ObjectMapper mapper, JsonNode node) {
JsonNode eventTypeNode = node.get("eventType");
if (eventTypeNode == null) {
System.err.println(format("{0}: Invalid message, missing eventType", discoveryName));
return;
}
switch (eventTypeNode.asText()) {
case "error":
try {
PluggableDiscoveryMessage msg = mapper.treeToValue(node, PluggableDiscoveryMessage.class);
debug("error: " + msg.getMessage());
if (msg.getMessage().contains("START_SYNC")) {
startPolling();
}
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return;
case "list":
JsonNode portsNode = node.get("ports");
if (portsNode == null) {
System.err.println(format("{0}: Invalid message, missing ports list", discoveryName));
return;
}
if (!portsNode.isArray()) {
System.err.println(format("{0}: Invalid message, ports list should be an array", discoveryName));
return;
}
synchronized (portList) {
portList.clear();
}
portsNode.forEach(portNode -> {
BoardPort port = mapJsonNodeToBoardPort(mapper, node);
if (port != null) {
addOrUpdate(port);
}
});
return;
// Messages for SYNC updates
case "add":
BoardPort addedPort = mapJsonNodeToBoardPort(mapper, node);
if (addedPort != null) {
addOrUpdate(addedPort);
}
return;
case "remove":
BoardPort removedPort = mapJsonNodeToBoardPort(mapper, node);
if (removedPort != null) {
remove(removedPort);
}
return;
default:
debug("Invalid event: " + eventTypeNode.asText());
return;
}
}
private BoardPort mapJsonNodeToBoardPort(ObjectMapper mapper, JsonNode node) {
try {
BoardPort port = mapper.treeToValue(node.get("port"), BoardPort.class);
// if no label, use address
if (port.getLabel() == null || port.getLabel().isEmpty()) {
port.setLabel(port.getAddress());
}
port.searchMatchingBoard();
return port;
} catch (JsonProcessingException e) {
System.err.println(format("{0}: Invalid BoardPort message", discoveryName));
e.printStackTrace();
return null;
}
}
@Override
public void start() throws Exception {
try {
debug("Starting: " + StringUtils.join(cmd, " "));
program = Runtime.getRuntime().exec(cmd);
} catch (Exception e) {
program = null;
return;
}
debug("START_SYNC");
write("START_SYNC\n");
pollingThread = null;
}
private void startPolling() {
// Discovery tools not supporting START_SYNC require a periodic
// LIST command. A second thread is created to send these
// commands, while the run() thread above listens for the
// discovery tool output.
debug("START");
write("START\n");
Thread pollingThread = new Thread() {
public void run() {
try {
while (program != null && program.isAlive()) {
debug("LIST");
write("LIST\n");
sleep(2500);
}
} catch (Exception e) {
}
}
};
pollingThread.start();
}
@Override
public void stop() throws Exception {
if (pollingThread != null) {
pollingThread.interrupt();
pollingThread = null;
}
write("STOP\n");
if (program != null) {
program.destroy();
program = null;
}
}
private void write(String command) {
if (program != null && program.isAlive()) {
OutputStream out = program.getOutputStream();
try {
out.write(command.getBytes());
out.flush();
} catch (Exception e) {
}
}
}
private void addOrUpdate(BoardPort port) {
String address = port.getAddress();
if (address == null)
return; // address required for "add" & "remove"
synchronized (portList) {
// if address already on the list, discard old info
portList.removeIf(bp -> address.equals(bp.getAddress()));
portList.add(port);
}
}
private void remove(BoardPort port) {
String address = port.getAddress();
if (address == null)
return; // address required for "add" & "remove"
synchronized (portList) {
portList.removeIf(bp -> address.equals(bp.getAddress()));
}
}
@Override
public List<BoardPort> listDiscoveredBoards() {
synchronized (portList) {
return new ArrayList<>(portList);
}
}
@Override
public List<BoardPort> listDiscoveredBoards(boolean complete) {
// XXX: parameter "complete "is really needed?
// should be checked on all existing discoveries
synchronized (portList) {
return new ArrayList<>(portList);
}
}
@Override
public String toString() {
return discoveryName;
}
}

View File

@ -0,0 +1,43 @@
/*
* This file is part of Arduino.
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2018 Arduino SA (http://www.arduino.cc/)
*/
package cc.arduino.packages.discoverers;
public class PluggableDiscoveryMessage {
private String eventType; // "add", "remove", "error"
private String message; // optional message, e.g. "START_SYNC not supported"
public String getEventType() {
return eventType;
}
public String getMessage() {
return message;
}
}

View File

@ -1,103 +0,0 @@
/*
* This file is part of Arduino.
*
* Arduino is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*
* Copyright 2013 Arduino LLC (http://www.arduino.cc/)
*/
package cc.arduino.packages.discoverers;
import cc.arduino.packages.BoardPort;
import cc.arduino.packages.Discovery;
import cc.arduino.packages.discoverers.serial.SerialBoardsLister;
import java.util.LinkedList;
import java.util.List;
import java.util.Timer;
public class SerialDiscovery implements Discovery, Runnable {
private Timer serialBoardsListerTimer;
private final List<BoardPort> serialBoardPorts;
private SerialBoardsLister serialBoardsLister = new SerialBoardsLister(this);
public SerialDiscovery() {
this.serialBoardPorts = new LinkedList<>();
}
@Override
public List<BoardPort> listDiscoveredBoards() {
return getSerialBoardPorts(false);
}
@Override
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) {
serialBoardPorts.clear();
serialBoardPorts.addAll(newSerialBoardPorts);
}
public void forceRefresh() {
serialBoardsLister.retriggerDiscovery(false);
}
public void setUploadInProgress(boolean param) {
serialBoardsLister.uploadInProgress = param;
}
public void pausePolling(boolean param) { serialBoardsLister.pausePolling = param;}
@Override
public void run() {
start();
}
@Override
public void start() {
this.serialBoardsListerTimer = new Timer(SerialBoardsLister.class.getName());
serialBoardsLister.start(serialBoardsListerTimer);
}
@Override
public void stop() {
this.serialBoardsListerTimer.purge();
}
}

View File

@ -29,41 +29,90 @@
package cc.arduino.packages.discoverers.serial;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import cc.arduino.packages.BoardPort;
import cc.arduino.packages.discoverers.SerialDiscovery;
import cc.arduino.packages.Discovery;
import processing.app.BaseNoGui;
import processing.app.Platform;
import processing.app.debug.TargetBoard;
import java.util.*;
public class SerialDiscovery implements Discovery, Runnable {
public class SerialBoardsLister extends TimerTask {
private final SerialDiscovery serialDiscovery;
private final List<BoardPort> boardPorts = new LinkedList<>();
private List<String> oldPorts = new LinkedList<>();
private Timer serialBoardsListerTimer;
private final List<BoardPort> serialBoardPorts = new ArrayList<>();
private final List<BoardPort> boardPorts = new ArrayList<>();
private final List<String> oldPorts = new ArrayList<>();
public boolean uploadInProgress = false;
public boolean pausePolling = false;
private BoardPort oldUploadBoardPort = null;
public SerialBoardsLister(SerialDiscovery serialDiscovery) {
this.serialDiscovery = serialDiscovery;
@Override
public List<BoardPort> listDiscoveredBoards() {
return listDiscoveredBoards(false);
}
public void start(Timer timer) {
timer.schedule(this, 0, 1000);
@Override
public List<BoardPort> listDiscoveredBoards(boolean complete) {
if (complete) {
return new ArrayList<>(serialBoardPorts);
}
List<BoardPort> onlineBoardPorts = new ArrayList<>();
for (BoardPort port : serialBoardPorts) {
if (port.isOnline() == true) {
onlineBoardPorts.add(port);
}
}
return onlineBoardPorts;
}
public synchronized void retriggerDiscovery(boolean polled) {
public void setSerialBoardPorts(List<BoardPort> newSerialBoardPorts) {
serialBoardPorts.clear();
serialBoardPorts.addAll(newSerialBoardPorts);
}
public void setUploadInProgress(boolean param) {
uploadInProgress = param;
}
public void pausePolling(boolean param) {
pausePolling = param;
}
@Override
public void run() {
start();
}
@Override
public void start() {
serialBoardsListerTimer = new Timer(SerialDiscovery.class.getName());
serialBoardsListerTimer.schedule(new TimerTask() {
@Override
public void run() {
if (BaseNoGui.packages != null && !pausePolling) {
forceRefresh();
}
}
}, 0, 1000);
}
@Override
public void stop() {
serialBoardsListerTimer.cancel();
}
public synchronized void forceRefresh() {
Platform platform = BaseNoGui.getPlatform();
if (platform == null) {
return;
}
if (polled && pausePolling) {
return;
}
List<String> ports = platform.listSerials();
if (ports.equals(oldPorts)) {
return;
@ -113,19 +162,18 @@ public class SerialBoardsLister extends TimerTask {
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) {
if (boardPort == null) {
boardPort = new BoardPort();
boardPorts.add(boardPort);
}
boardPort.setAddress(port);
boardPort.setProtocol("serial");
@ -136,50 +184,35 @@ public class SerialBoardsLister extends TimerTask {
if (boardData != null) {
boardPort.getPrefs().put("vid", boardData.get("vid").toString());
boardPort.getPrefs().put("pid", boardData.get("pid").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) {
String boardName = board.getName();
if (boardName != null) {
label += " (" + boardName + ")";
}
boardPort.setBoardName(boardName);
}
} else {
if (!parts[1].equals("0000")) {
boardPort.setVIDPID(parts[1], parts[2]);
boardPort.getPrefs().put("vid", parts[1]);
boardPort.getPrefs().put("pid", parts[2]);
// ask Cloud API to match the board with known VID/PID pair
platform.getBoardWithMatchingVidPidFromCloud(parts[1], parts[2]);
} else {
boardPort.setVIDPID("0000", "0000");
boardPort.setISerial("");
boardPort.getPrefs().put("vid", "0000");
boardPort.getPrefs().put("pid", "0000");
boardPort.getPrefs().put("iserial", "");
}
}
boardPort.setLabel(label);
if (!updatingInfos) {
boardPorts.add(boardPort);
}
}
serialDiscovery.setSerialBoardPorts(boardPorts);
}
@Override
public void run() {
if (BaseNoGui.packages == null) {
return;
}
retriggerDiscovery(true);
setSerialBoardPorts(boardPorts);
}
}

View File

@ -223,7 +223,7 @@ public class BaseNoGui {
public static DiscoveryManager getDiscoveryManager() {
if (discoveryManager == null) {
discoveryManager = new DiscoveryManager();
discoveryManager = new DiscoveryManager(packages);
}
return discoveryManager;
}
@ -506,7 +506,7 @@ public class BaseNoGui {
}
if (discoveryManager == null) {
discoveryManager = new DiscoveryManager();
discoveryManager = new DiscoveryManager(packages);
}
}

View File

@ -100,4 +100,8 @@ public class LegacyTargetBoard implements TargetBoard {
return containerPlatform;
}
@Override
public String getFQBN() {
return getContainerPlatform().getContainerPackage().getId() + ":" + getContainerPlatform().getId() + ":" + getId();
}
}

View File

@ -92,4 +92,6 @@ public interface TargetBoard {
public TargetPlatform getContainerPlatform();
public String getFQBN();
}

View File

@ -41,6 +41,7 @@ public class CommandlineParser {
private String getPref;
private String boardToInstall;
private String libraryToInstall;
private Optional<String> uploadPort = Optional.empty();
private final List<String> filenames = new LinkedList<>();
public CommandlineParser(String[] args) {
@ -141,7 +142,7 @@ public class CommandlineParser {
i++;
if (i >= args.length)
BaseNoGui.showError(null, tr("Argument required for --port"), 3);
BaseNoGui.selectSerialPort(args[i]);
uploadPort = Optional.of(args[i]);
if (action == ACTION.GUI)
action = ACTION.NOOP;
continue;
@ -356,4 +357,8 @@ public class CommandlineParser {
public boolean isPreserveTempFiles() {
return preserveTempFiles;
}
public Optional<String> getUploadPort() {
return uploadPort;
}
}