From 70bbe398abc969352f10d9a4328829a3c73b8a07 Mon Sep 17 00:00:00 2001 From: Federico Fissore Date: Tue, 24 Mar 2015 12:50:26 +0100 Subject: [PATCH] Network and Serial board ports discovery is now asynchronous, hence it does not block "tools" menu any more. Fixes #2788 --- .../src/cc/arduino/packages/Discovery.java | 15 +-- .../cc/arduino/packages/DiscoveryManager.java | 4 +- .../discoverers/NetworkDiscovery.java | 97 +++++++-------- .../packages/discoverers/SerialDiscovery.java | 82 ++++--------- .../network/BoardReachabilityFilter.java | 83 +++++++++++++ .../serial/SerialBoardsLister.java | 113 ++++++++++++++++++ .../src/processing/app/helpers/NetUtils.java | 4 +- 7 files changed, 270 insertions(+), 128 deletions(-) create mode 100644 arduino-core/src/cc/arduino/packages/discoverers/network/BoardReachabilityFilter.java create mode 100644 arduino-core/src/cc/arduino/packages/discoverers/serial/SerialBoardsLister.java diff --git a/arduino-core/src/cc/arduino/packages/Discovery.java b/arduino-core/src/cc/arduino/packages/Discovery.java index 633c1fcc7..eb4b41da2 100644 --- a/arduino-core/src/cc/arduino/packages/Discovery.java +++ b/arduino-core/src/cc/arduino/packages/Discovery.java @@ -29,36 +29,27 @@ package cc.arduino.packages; -import processing.app.helpers.PreferencesMap; - import java.util.List; public interface Discovery { - /** - * Set discovery preferences - * - * @param options - */ - public void setPreferences(PreferencesMap options); - /** * Start discovery service * * @throws Exception */ - public void start() throws Exception; + void start() throws Exception; /** * Stop discovery service */ - public void stop() throws Exception; + void stop() throws Exception; /** * Return the list of discovered ports. * * @return */ - public List discovery(); + List listDiscoveredBoards(); } diff --git a/arduino-core/src/cc/arduino/packages/DiscoveryManager.java b/arduino-core/src/cc/arduino/packages/DiscoveryManager.java index 5c45314a1..ddb774b94 100644 --- a/arduino-core/src/cc/arduino/packages/DiscoveryManager.java +++ b/arduino-core/src/cc/arduino/packages/DiscoveryManager.java @@ -63,7 +63,7 @@ public class DiscoveryManager { try { d.stop(); } catch (Exception e) { - e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + e.printStackTrace(); //just printing as the JVM is terminating } } } @@ -74,7 +74,7 @@ public class DiscoveryManager { public List discovery() { List res = new ArrayList(); for (Discovery d : discoverers) { - res.addAll(d.discovery()); + res.addAll(d.listDiscoveredBoards()); } return res; } diff --git a/arduino-core/src/cc/arduino/packages/discoverers/NetworkDiscovery.java b/arduino-core/src/cc/arduino/packages/discoverers/NetworkDiscovery.java index 6f593571a..c913a410d 100644 --- a/arduino-core/src/cc/arduino/packages/discoverers/NetworkDiscovery.java +++ b/arduino-core/src/cc/arduino/packages/discoverers/NetworkDiscovery.java @@ -31,9 +31,9 @@ package cc.arduino.packages.discoverers; import cc.arduino.packages.BoardPort; import cc.arduino.packages.Discovery; +import cc.arduino.packages.discoverers.network.BoardReachabilityFilter; import cc.arduino.packages.discoverers.network.NetworkChecker; import processing.app.BaseNoGui; -import processing.app.helpers.NetUtils; import processing.app.helpers.PreferencesMap; import processing.app.zeroconf.jmdns.ArduinoDNSTaskStarter; @@ -41,70 +41,55 @@ import javax.jmdns.*; import javax.jmdns.impl.DNSTaskStarter; import java.io.IOException; import java.net.InetAddress; -import java.net.UnknownHostException; import java.util.*; public class NetworkDiscovery implements Discovery, ServiceListener, cc.arduino.packages.discoverers.network.NetworkTopologyListener { - private Timer timer; - private final List ports; + private final List boardPortsDiscoveredWithJmDNS; private final Map mappedJmDNSs; + private Timer networkCheckerTimer; + private Timer boardReachabilityFilterTimer; + private final List reachableBoardPorts; public NetworkDiscovery() { DNSTaskStarter.Factory.setClassDelegate(new ArduinoDNSTaskStarter()); - this.ports = new ArrayList(); + this.boardPortsDiscoveredWithJmDNS = new LinkedList(); this.mappedJmDNSs = new Hashtable(); + this.reachableBoardPorts = new LinkedList(); } @Override - public List discovery() { - List boardPorts = clonePortsList(); - Iterator boardPortIterator = boardPorts.iterator(); - while (boardPortIterator.hasNext()) { - try { - BoardPort board = boardPortIterator.next(); - - InetAddress inetAddress = InetAddress.getByName(board.getAddress()); - int broadcastedPort = Integer.valueOf(board.getPrefs().get("port")); - - List ports = new LinkedList(); - ports.add(broadcastedPort); - - //dirty code: allows non up to date yuns to be discovered. Newer yuns will broadcast port 22 - if (broadcastedPort == 80) { - ports.add(0, 22); - } - - boolean reachable = NetUtils.isReachable(inetAddress, ports); - if (!reachable) { - boardPortIterator.remove(); - } - } catch (UnknownHostException e) { - boardPortIterator.remove(); - } - } - return boardPorts; - } - - private List clonePortsList() { - synchronized (this) { - return new ArrayList(this.ports); + public List listDiscoveredBoards() { + synchronized (reachableBoardPorts) { + return new LinkedList(reachableBoardPorts); } } - @Override - public void setPreferences(PreferencesMap options) { + public void setReachableBoardPorts(List newReachableBoardPorts) { + synchronized (reachableBoardPorts) { + this.reachableBoardPorts.clear(); + this.reachableBoardPorts.addAll(newReachableBoardPorts); + } + } + + public List getBoardPortsDiscoveredWithJmDNS() { + synchronized (boardPortsDiscoveredWithJmDNS) { + return new LinkedList(boardPortsDiscoveredWithJmDNS); + } } @Override public void start() throws IOException { - this.timer = new Timer(this.getClass().getName() + " timer"); - new NetworkChecker(this, NetworkTopologyDiscovery.Factory.getInstance()).start(timer); + this.networkCheckerTimer = new Timer(NetworkChecker.class.getName()); + new NetworkChecker(this, NetworkTopologyDiscovery.Factory.getInstance()).start(networkCheckerTimer); + this.boardReachabilityFilterTimer = new Timer(BoardReachabilityFilter.class.getName()); + new BoardReachabilityFilter(this).start(boardReachabilityFilterTimer); } @Override public void stop() throws IOException { - timer.purge(); + this.networkCheckerTimer.purge(); + this.boardReachabilityFilterTimer.purge(); // we don't close each JmDNS instance as it's too slow } @@ -125,10 +110,11 @@ public class NetworkDiscovery implements Discovery, ServiceListener, cc.arduino. @Override public void serviceRemoved(ServiceEvent serviceEvent) { String name = serviceEvent.getName(); - synchronized (this) { - for (BoardPort port : ports) { - if (port.getBoardName().equals(name)) - ports.remove(port); + synchronized (boardPortsDiscoveredWithJmDNS) { + for (BoardPort port : boardPortsDiscoveredWithJmDNS) { + if (port.getBoardName().equals(name)) { + boardPortsDiscoveredWithJmDNS.remove(port); + } } } } @@ -147,10 +133,9 @@ public class NetworkDiscovery implements Discovery, ServiceListener, cc.arduino. board = info.getPropertyString("board"); prefs.put("board", board); prefs.put("distro_version", info.getPropertyString("distro_version")); + prefs.put("port", "" + info.getPort()); } - prefs.put("port", "" + info.getPort()); - String label = name + " at " + address; if (board != null) { String boardName = BaseNoGui.getPlatform().resolveDeviceByBoardID(BaseNoGui.packages, board); @@ -166,19 +151,21 @@ public class NetworkDiscovery implements Discovery, ServiceListener, cc.arduino. port.setPrefs(prefs); port.setLabel(label); - synchronized (this) { + synchronized (boardPortsDiscoveredWithJmDNS) { removeDuplicateBoards(port); - ports.add(port); + boardPortsDiscoveredWithJmDNS.add(port); } } } private void removeDuplicateBoards(BoardPort newBoard) { - Iterator iterator = ports.iterator(); - while (iterator.hasNext()) { - BoardPort board = iterator.next(); - if (newBoard.getAddress().equals(board.getAddress())) { - iterator.remove(); + synchronized (boardPortsDiscoveredWithJmDNS) { + Iterator iterator = boardPortsDiscoveredWithJmDNS.iterator(); + while (iterator.hasNext()) { + BoardPort board = iterator.next(); + if (newBoard.getAddress().equals(board.getAddress())) { + iterator.remove(); + } } } } diff --git a/arduino-core/src/cc/arduino/packages/discoverers/SerialDiscovery.java b/arduino-core/src/cc/arduino/packages/discoverers/SerialDiscovery.java index 6c9cc8450..47b7a848e 100644 --- a/arduino-core/src/cc/arduino/packages/discoverers/SerialDiscovery.java +++ b/arduino-core/src/cc/arduino/packages/discoverers/SerialDiscovery.java @@ -31,16 +31,11 @@ package cc.arduino.packages.discoverers; import cc.arduino.packages.BoardPort; import cc.arduino.packages.Discovery; -import processing.app.BaseNoGui; -import processing.app.Platform; -import processing.app.Serial; -import processing.app.debug.TargetBoard; -import processing.app.helpers.PreferencesMap; +import cc.arduino.packages.discoverers.serial.SerialBoardsLister; -import java.util.ArrayList; -import java.util.HashMap; +import java.util.LinkedList; import java.util.List; -import java.util.Map; +import java.util.Timer; import static processing.app.I18n._; @@ -51,66 +46,39 @@ public class SerialDiscovery implements Discovery { _("Uncertified"); } - @Override - public List discovery() { - Platform os = BaseNoGui.getPlatform(); - String devicesListOutput = os.preListAllCandidateDevices(); + private Timer serialBoardsListerTimer; + private final List serialBoardPorts; - List res = new ArrayList(); - - List ports = Serial.list(); - - for (String port : ports) { - Map boardData = os.resolveDeviceAttachedTo(port, BaseNoGui.packages, devicesListOutput); - - BoardPort boardPort = new BoardPort(); - boardPort.setAddress(port); - boardPort.setProtocol("serial"); - - String label = port; - - PreferencesMap prefs = new PreferencesMap(); - - if (boardData != null) { - prefs.put("vid", boardData.get("vid").toString()); - prefs.put("pid", boardData.get("pid").toString()); - - TargetBoard board = (TargetBoard) boardData.get("board"); - if (board != null) { - String warningKey = "vid." + boardData.get("vid").toString() + ".warning"; - String warning = board.getPreferences().get(warningKey); - prefs.put("warning", warning); - - String boardName = board.getName(); - if (boardName != null) { - if (warning != null) { - label += " (" + boardName + " - " + _(warning) + ")"; - } else { - label += " (" + boardName + ")"; - } - } - boardPort.setBoardName(boardName); - } - } - - boardPort.setLabel(label); - boardPort.setPrefs(prefs); - - res.add(boardPort); - } - return res; + public SerialDiscovery() { + this.serialBoardPorts = new LinkedList(); } @Override - public void setPreferences(PreferencesMap options) { + public List listDiscoveredBoards() { + return getSerialBoardPorts(); + } + + public List getSerialBoardPorts() { + synchronized (serialBoardPorts) { + return new LinkedList(serialBoardPorts); + } + } + + public void setSerialBoardPorts(List newSerialBoardPorts) { + synchronized (serialBoardPorts) { + serialBoardPorts.clear(); + serialBoardPorts.addAll(newSerialBoardPorts); + } } @Override public void start() { + this.serialBoardsListerTimer = new Timer(SerialBoardsLister.class.getName()); + new SerialBoardsLister(this).start(serialBoardsListerTimer); } @Override public void stop() { + this.serialBoardsListerTimer.purge(); } - } diff --git a/arduino-core/src/cc/arduino/packages/discoverers/network/BoardReachabilityFilter.java b/arduino-core/src/cc/arduino/packages/discoverers/network/BoardReachabilityFilter.java new file mode 100644 index 000000000..fbc3e0813 --- /dev/null +++ b/arduino-core/src/cc/arduino/packages/discoverers/network/BoardReachabilityFilter.java @@ -0,0 +1,83 @@ +/* + * 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 2015 Arduino LLC (http://www.arduino.cc/) + */ + +package cc.arduino.packages.discoverers.network; + +import cc.arduino.packages.BoardPort; +import cc.arduino.packages.discoverers.NetworkDiscovery; +import processing.app.helpers.NetUtils; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.*; + +public class BoardReachabilityFilter extends TimerTask { + + private final NetworkDiscovery networkDiscovery; + + public BoardReachabilityFilter(NetworkDiscovery networkDiscovery) { + this.networkDiscovery = networkDiscovery; + } + + public void start(Timer timer) { + timer.schedule(this, 0, 3000); + } + + @Override + public void run() { + List boardPorts = networkDiscovery.getBoardPortsDiscoveredWithJmDNS(); + + Iterator boardPortIterator = boardPorts.iterator(); + while (boardPortIterator.hasNext()) { + try { + BoardPort board = boardPortIterator.next(); + + InetAddress inetAddress = InetAddress.getByName(board.getAddress()); + int broadcastedPort = Integer.valueOf(board.getPrefs().get("port")); + + List ports = new LinkedList(); + ports.add(broadcastedPort); + + //dirty code: allows non up to date yuns to be discovered. Newer yuns will broadcast port 22 + if (broadcastedPort == 80) { + ports.add(0, 22); + } + + boolean reachable = NetUtils.isReachable(inetAddress, ports); + if (!reachable) { + boardPortIterator.remove(); + } + } catch (UnknownHostException e) { + boardPortIterator.remove(); + } + } + + networkDiscovery.setReachableBoardPorts(boardPorts); + } +} diff --git a/arduino-core/src/cc/arduino/packages/discoverers/serial/SerialBoardsLister.java b/arduino-core/src/cc/arduino/packages/discoverers/serial/SerialBoardsLister.java new file mode 100644 index 000000000..08dc3af3e --- /dev/null +++ b/arduino-core/src/cc/arduino/packages/discoverers/serial/SerialBoardsLister.java @@ -0,0 +1,113 @@ +/* + * 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 2015 Arduino LLC (http://www.arduino.cc/) + */ + +package cc.arduino.packages.discoverers.serial; + +import cc.arduino.packages.BoardPort; +import cc.arduino.packages.discoverers.SerialDiscovery; +import processing.app.BaseNoGui; +import processing.app.Platform; +import processing.app.Serial; +import processing.app.debug.TargetBoard; +import processing.app.helpers.PreferencesMap; + +import java.util.*; + +import static processing.app.I18n._; + +public class SerialBoardsLister extends TimerTask { + + private final SerialDiscovery serialDiscovery; + + public SerialBoardsLister(SerialDiscovery serialDiscovery) { + this.serialDiscovery = serialDiscovery; + } + + public void start(Timer timer) { + timer.schedule(this, 0, 3000); + } + + @Override + public void run() { + Platform platform = BaseNoGui.getPlatform(); + if (platform == null) { + return; + } + + List boardPorts = new LinkedList(); + + List ports = Serial.list(); + + String devicesListOutput = null; + if (!ports.isEmpty()) { + devicesListOutput = platform.preListAllCandidateDevices(); + } + + for (String port : ports) { + Map boardData = platform.resolveDeviceAttachedTo(port, BaseNoGui.packages, devicesListOutput); + + BoardPort boardPort = new BoardPort(); + boardPort.setAddress(port); + boardPort.setProtocol("serial"); + + String label = port; + + PreferencesMap prefs = new PreferencesMap(); + + if (boardData != null) { + prefs.put("vid", boardData.get("vid").toString()); + prefs.put("pid", boardData.get("pid").toString()); + + TargetBoard board = (TargetBoard) boardData.get("board"); + if (board != null) { + String warningKey = "vid." + boardData.get("vid").toString() + ".warning"; + String warning = board.getPreferences().get(warningKey); + prefs.put("warning", warning); + + String boardName = board.getName(); + if (boardName != null) { + if (warning != null) { + label += " (" + boardName + " - " + _(warning) + ")"; + } else { + label += " (" + boardName + ")"; + } + } + boardPort.setBoardName(boardName); + } + } + + boardPort.setLabel(label); + boardPort.setPrefs(prefs); + + boardPorts.add(boardPort); + } + + serialDiscovery.setSerialBoardPorts(boardPorts); + } +} diff --git a/arduino-core/src/processing/app/helpers/NetUtils.java b/arduino-core/src/processing/app/helpers/NetUtils.java index 67201bf3d..17dc52c86 100644 --- a/arduino-core/src/processing/app/helpers/NetUtils.java +++ b/arduino-core/src/processing/app/helpers/NetUtils.java @@ -11,7 +11,7 @@ public abstract class NetUtils { private static boolean isReachableByEcho(InetAddress address) { try { - return address.isReachable(100); + return address.isReachable(300); } catch (IOException e) { return false; } @@ -38,7 +38,7 @@ public abstract class NetUtils { Socket socket = null; try { socket = new Socket(); - socket.connect(new InetSocketAddress(address, port), 300); + socket.connect(new InetSocketAddress(address, port), 1000); return true; } catch (IOException e) { return false;