1 /* jSSC (Java Simple Serial Connector) - serial port communication library. 2 * (C) Alexey Sokolov (scream3r), 2010-2014. 3 * 4 * Patched for Arduino by Cristian Maglie. 5 * 6 * This file is part of jSSC. 7 * 8 * jSSC is free software: you can redistribute it and/or modify 9 * it under the terms of the GNU Lesser General Public License as published by 10 * the Free Software Foundation, either version 3 of the License, or 11 * (at your option) any later version. 12 * 13 * jSSC is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU Lesser General Public License for more details. 17 * 18 * You should have received a copy of the GNU Lesser General Public License 19 * along with jSSC. If not, see <http://www.gnu.org/licenses/>. 20 * 21 * If you use jSSC in public project you can inform me about this by e-mail, 22 * of course if you want it. 23 * 24 * e-mail: scream3r.org@gmail.com 25 * web-site: http://scream3r.org | http://code.google.com/p/java-simple-serial-connector/ 26 */ 27 package processing.app; 28 29 import java.io.File; 30 import java.util.Comparator; 31 import java.util.TreeSet; 32 import java.util.regex.Pattern; 33 34 import jssc.SerialNativeInterface; 35 36 /** 37 * 38 * @author scream3r 39 */ 40 public class SerialPortList { 41 42 private static SerialNativeInterface serialInterface; 43 private static final Pattern PORTNAMES_REGEXP; 44 private static final String PORTNAMES_PATH; 45 46 static { 47 serialInterface = new SerialNativeInterface(); 48 switch (SerialNativeInterface.getOsType()) { 49 case SerialNativeInterface.OS_LINUX: { 50 PORTNAMES_REGEXP = Pattern.compile("(ttyS|ttyUSB|ttyACM|ttyAMA|rfcomm|ttyO)[0-9]{1,3}"); 51 PORTNAMES_PATH = "/dev/"; 52 break; 53 } 54 case SerialNativeInterface.OS_SOLARIS: { 55 PORTNAMES_REGEXP = Pattern.compile("[0-9]*|[a-z]*"); 56 PORTNAMES_PATH = "/dev/term/"; 57 break; 58 } 59 case SerialNativeInterface.OS_MAC_OS_X: { 60 PORTNAMES_REGEXP = Pattern.compile("(tty|cu)\\..*"); 61 PORTNAMES_PATH = "/dev/"; 62 break; 63 } 64 case SerialNativeInterface.OS_WINDOWS: { 65 PORTNAMES_REGEXP = Pattern.compile(""); 66 PORTNAMES_PATH = ""; 67 break; 68 } 69 default: { 70 PORTNAMES_REGEXP = null; 71 PORTNAMES_PATH = null; 72 break; 73 } 74 } 75 } 76 77 //since 2.1.0 -> Fully rewrited port name comparator 78 private static final Comparator<String> PORTNAMES_COMPARATOR = new Comparator<String>() { 79 80 @Override 81 public int compare(String valueA, String valueB) { 82 83 if(valueA.equalsIgnoreCase(valueB)){ 84 return valueA.compareTo(valueB); 85 } 86 87 int minLength = Math.min(valueA.length(), valueB.length()); 88 89 int shiftA = 0; 90 int shiftB = 0; 91 92 for(int i = 0; i < minLength; i++){ 93 char charA = valueA.charAt(i - shiftA); 94 char charB = valueB.charAt(i - shiftB); 95 if(charA != charB){ 96 if(Character.isDigit(charA) && Character.isDigit(charB)){ 97 int[] resultsA = getNumberAndLastIndex(valueA, i - shiftA); 98 int[] resultsB = getNumberAndLastIndex(valueB, i - shiftB); 99 100 if(resultsA[0] != resultsB[0]){ 101 return resultsA[0] - resultsB[0]; 102 } 103 104 if(valueA.length() < valueB.length()){ 105 i = resultsA[1]; 106 shiftB = resultsA[1] - resultsB[1]; 107 } 108 else { 109 i = resultsB[1]; 110 shiftA = resultsB[1] - resultsA[1]; 111 } 112 } 113 else { 114 if(Character.toLowerCase(charA) - Character.toLowerCase(charB) != 0){ 115 return Character.toLowerCase(charA) - Character.toLowerCase(charB); 116 } 117 } 118 } 119 } 120 return valueA.compareToIgnoreCase(valueB); 121 } 122 123 /** 124 * Evaluate port <b>index/number</b> from <b>startIndex</b> to the number end. For example: 125 * for port name <b>serial-123-FF</b> you should invoke this method with <b>startIndex = 7</b> 126 * 127 * @return If port <b>index/number</b> correctly evaluated it value will be returned<br> 128 * <b>returnArray[0] = index/number</b><br> 129 * <b>returnArray[1] = stopIndex</b><br> 130 * 131 * If incorrect:<br> 132 * <b>returnArray[0] = -1</b><br> 133 * <b>returnArray[1] = startIndex</b><br> 134 * 135 * For this name <b>serial-123-FF</b> result is: 136 * <b>returnArray[0] = 123</b><br> 137 * <b>returnArray[1] = 10</b><br> 138 */ 139 private int[] getNumberAndLastIndex(String str, int startIndex) { 140 String numberValue = ""; 141 int[] returnValues = {-1, startIndex}; 142 for(int i = startIndex; i < str.length(); i++){ 143 returnValues[1] = i; 144 char c = str.charAt(i); 145 if(Character.isDigit(c)){ 146 numberValue += c; 147 } 148 else { 149 break; 150 } 151 } 152 try { 153 returnValues[0] = Integer.valueOf(numberValue); 154 } 155 catch (Exception ex) { 156 //Do nothing 157 } 158 return returnValues; 159 } 160 }; 161 //<-since 2.1.0 162 163 /** 164 * Get sorted array of serial ports in the system using default settings:<br> 165 * 166 * <b>Search path</b><br> 167 * Windows - ""(always ignored)<br> 168 * Linux - "/dev/"<br> 169 * Solaris - "/dev/term/"<br> 170 * MacOSX - "/dev/"<br> 171 * 172 * <b>RegExp</b><br> 173 * Windows - ""<br> 174 * Linux - "(ttyS|ttyUSB|ttyACM|ttyAMA|rfcomm)[0-9]{1,3}"<br> 175 * Solaris - "[0-9]*|[a-z]*"<br> 176 * MacOSX - "tty.(serial|usbserial|usbmodem).*"<br> 177 * 178 * @return String array. If there is no ports in the system String[] 179 * with <b>zero</b> length will be returned (since jSSC-0.8 in previous versions null will be returned) 180 */ getPortNames()181 public static String[] getPortNames() { 182 return getPortNames(PORTNAMES_PATH, PORTNAMES_REGEXP, PORTNAMES_COMPARATOR); 183 } 184 185 /** 186 * Get sorted array of serial ports in the system located on searchPath 187 * 188 * @param searchPath Path for searching serial ports <b>(not null)</b><br> 189 * The default search paths:<br> 190 * Linux, MacOSX: <b>/dev/</b><br> 191 * Solaris: <b>/dev/term/</b><br> 192 * Windows: <b>this parameter ingored</b> 193 * 194 * @return String array. If there is no ports in the system String[] 195 * 196 * @since 2.3.0 197 */ getPortNames(String searchPath)198 public static String[] getPortNames(String searchPath) { 199 return getPortNames(searchPath, PORTNAMES_REGEXP, PORTNAMES_COMPARATOR); 200 } 201 202 /** 203 * Get sorted array of serial ports in the system matched pattern 204 * 205 * @param pattern RegExp pattern for matching port names <b>(not null)</b> 206 * 207 * @return String array. If there is no ports in the system String[] 208 * 209 * @since 2.3.0 210 */ getPortNames(Pattern pattern)211 public static String[] getPortNames(Pattern pattern) { 212 return getPortNames(PORTNAMES_PATH, pattern, PORTNAMES_COMPARATOR); 213 } 214 215 /** 216 * Get sorted array of serial ports in the system matched pattern 217 * 218 * @param comparator Comparator for sotring port names <b>(not null)</b> 219 * 220 * @return String array. If there is no ports in the system String[] 221 * 222 * @since 2.3.0 223 */ getPortNames(Comparator<String> comparator)224 public static String[] getPortNames(Comparator<String> comparator) { 225 return getPortNames(PORTNAMES_PATH, PORTNAMES_REGEXP, comparator); 226 } 227 228 /** 229 * Get sorted array of serial ports in the system located on searchPath, matched pattern 230 * 231 * @param searchPath Path for searching serial ports <b>(not null)</b><br> 232 * The default search paths:<br> 233 * Linux, MacOSX: <b>/dev/</b><br> 234 * Solaris: <b>/dev/term/</b><br> 235 * Windows: <b>this parameter ingored</b> 236 * @param pattern RegExp pattern for matching port names <b>(not null)</b> 237 * 238 * @return String array. If there is no ports in the system String[] 239 * 240 * @since 2.3.0 241 */ getPortNames(String searchPath, Pattern pattern)242 public static String[] getPortNames(String searchPath, Pattern pattern) { 243 return getPortNames(searchPath, pattern, PORTNAMES_COMPARATOR); 244 } 245 246 /** 247 * Get sorted array of serial ports in the system located on searchPath and sorted by comparator 248 * 249 * @param searchPath Path for searching serial ports <b>(not null)</b><br> 250 * The default search paths:<br> 251 * Linux, MacOSX: <b>/dev/</b><br> 252 * Solaris: <b>/dev/term/</b><br> 253 * Windows: <b>this parameter ingored</b> 254 * @param comparator Comparator for sotring port names <b>(not null)</b> 255 * 256 * @return String array. If there is no ports in the system String[] 257 * 258 * @since 2.3.0 259 */ getPortNames(String searchPath, Comparator<String> comparator)260 public static String[] getPortNames(String searchPath, Comparator<String> comparator) { 261 return getPortNames(searchPath, PORTNAMES_REGEXP, comparator); 262 } 263 264 /** 265 * Get sorted array of serial ports in the system matched pattern and sorted by comparator 266 * 267 * @param pattern RegExp pattern for matching port names <b>(not null)</b> 268 * @param comparator Comparator for sotring port names <b>(not null)</b> 269 * 270 * @return String array. If there is no ports in the system String[] 271 * 272 * @since 2.3.0 273 */ getPortNames(Pattern pattern, Comparator<String> comparator)274 public static String[] getPortNames(Pattern pattern, Comparator<String> comparator) { 275 return getPortNames(PORTNAMES_PATH, pattern, comparator); 276 } 277 278 /** 279 * Get sorted array of serial ports in the system located on searchPath, matched pattern and sorted by comparator 280 * 281 * @param searchPath Path for searching serial ports <b>(not null)</b><br> 282 * The default search paths:<br> 283 * Linux, MacOSX: <b>/dev/</b><br> 284 * Solaris: <b>/dev/term/</b><br> 285 * Windows: <b>this parameter ingored</b> 286 * @param pattern RegExp pattern for matching port names <b>(not null)</b> 287 * @param comparator Comparator for sotring port names <b>(not null)</b> 288 * 289 * @return String array. If there is no ports in the system String[] 290 * 291 * @since 2.3.0 292 */ getPortNames(String searchPath, Pattern pattern, Comparator<String> comparator)293 public static String[] getPortNames(String searchPath, Pattern pattern, Comparator<String> comparator) { 294 if(searchPath == null || pattern == null || comparator == null){ 295 return new String[]{}; 296 } 297 if(SerialNativeInterface.getOsType() == SerialNativeInterface.OS_WINDOWS){ 298 return getWindowsPortNames(pattern, comparator); 299 } 300 return getUnixBasedPortNames(searchPath, pattern, comparator); 301 } 302 303 /** 304 * Get serial port names in Windows 305 * 306 * @since 2.3.0 307 */ getWindowsPortNames(Pattern pattern, Comparator<String> comparator)308 private static String[] getWindowsPortNames(Pattern pattern, Comparator<String> comparator) { 309 String[] portNames = serialInterface.getSerialPortNames(); 310 if(portNames == null){ 311 return new String[]{}; 312 } 313 TreeSet<String> ports = new TreeSet<>(comparator); 314 for(String portName : portNames){ 315 if(pattern.matcher(portName).find()){ 316 ports.add(portName); 317 } 318 } 319 return ports.toArray(new String[ports.size()]); 320 } 321 322 /** 323 * Universal method for getting port names of _nix based systems 324 */ getUnixBasedPortNames(String searchPath, Pattern pattern, Comparator<String> comparator)325 private static String[] getUnixBasedPortNames(String searchPath, Pattern pattern, Comparator<String> comparator) { 326 searchPath = (searchPath.equals("") ? searchPath : (searchPath.endsWith("/") ? searchPath : searchPath + "/")); 327 String[] returnArray = new String[]{}; 328 File dir = new File(searchPath); 329 if(dir.exists() && dir.isDirectory()){ 330 File[] files = dir.listFiles(); 331 if(files.length > 0){ 332 TreeSet<String> portsTree = new TreeSet<>(comparator); 333 for(File file : files){ 334 String fileName = file.getName(); 335 if(!file.isDirectory() && !file.isFile() && pattern.matcher(fileName).find()){ 336 String portName = searchPath + fileName; 337 // For linux ttyS0..31 serial ports check existence by opening each of them 338 if (fileName.startsWith("ttyS")) { 339 long portHandle = serialInterface.openPort(portName, false);//Open port without TIOCEXCL 340 if(portHandle < 0 && portHandle != SerialNativeInterface.ERR_PORT_BUSY){ 341 continue; 342 } 343 else if(portHandle != SerialNativeInterface.ERR_PORT_BUSY) { 344 serialInterface.closePort(portHandle); 345 } 346 } 347 portsTree.add(portName); 348 } 349 } 350 returnArray = portsTree.toArray(returnArray); 351 } 352 } 353 return returnArray; 354 } 355 } 356