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