001package edu.wpi.first.wpilibj.networktables; 002 003import edu.wpi.first.wpilibj.networktables2.*; 004import edu.wpi.first.wpilibj.networktables2.thread.*; 005import edu.wpi.first.wpilibj.networktables2.util.*; 006import edu.wpi.first.wpilibj.networktables2.util.List; 007import edu.wpi.first.wpilibj.tables.*; 008import java.io.*; 009import java.util.*; 010 011/** 012 * 013 * @author Fredric 014 * @author mwills 015 * 016 */ 017 018public class NetworkTable implements ITable, IRemote { 019 private static final NTThreadManager threadManager = new DefaultThreadManager(); 020 021 /** 022 * The path separator for sub-tables and keys 023 * 024 */ 025 public static final char PATH_SEPARATOR = '/'; 026 /** 027 * The default port that network tables operates on 028 */ 029 public static final int DEFAULT_PORT = 1735; 030 031 032 033 034 035 036 037 private static NetworkTableProvider staticProvider = null; 038 039 private static NetworkTableMode mode = NetworkTableMode.Server; 040 private static int port = DEFAULT_PORT; 041 private static String ipAddress = null; 042 043 private synchronized static void checkInit(){ 044 if(staticProvider!=null) 045 throw new IllegalStateException("Network tables has already been initialized"); 046 } 047 /** 048 * @throws IOException 049 */ 050 public synchronized static void initialize() throws IOException { 051 checkInit(); 052 staticProvider = new NetworkTableProvider(mode.createNode(ipAddress, port, threadManager)); 053 } 054 055 /** 056 * set the table provider for static network tables methods 057 * This must be called before initalize or getTable 058 */ 059 public synchronized static void setTableProvider(NetworkTableProvider provider) { 060 checkInit(); 061 staticProvider = provider; 062 } 063 /** 064 * set that network tables should be a server 065 * This must be called before initalize or getTable 066 */ 067 public synchronized static void setServerMode(){ 068 checkInit(); 069 mode = NetworkTableMode.Server; 070 } 071 /** 072 * set that network tables should be a client 073 * This must be called before initalize or getTable 074 */ 075 public synchronized static void setClientMode(){ 076 checkInit(); 077 mode = NetworkTableMode.Client; 078 } 079 080 /** 081 * set the team the robot is configured for (this will set the ip address that network tables will connect to in client mode) 082 * This must be called before initalize or getTable 083 * @param team the team number 084 */ 085 public synchronized static void setTeam(int team){ 086 setIPAddress("10." + (team / 100) + "." + (team % 100) + ".2"); 087 } 088 /** 089 * @param address the adress that network tables will connect to in client mode 090 */ 091 public synchronized static void setIPAddress(final String address){ 092 checkInit(); 093 ipAddress = address; 094 } 095 /** 096 * Gets the table with the specified key. If the table does not exist, a new table will be created.<br> 097 * This will automatically initialize network tables if it has not been already 098 * 099 * @param key 100 * the key name 101 * @return the network table requested 102 */ 103 public synchronized static NetworkTable getTable(String key) { 104 if(staticProvider==null) 105 try { 106 initialize(); 107 } catch (IOException e) { 108 throw new RuntimeException("NetworkTable could not be initialized: "+e+": "+e.getMessage()); 109 } 110 return (NetworkTable)staticProvider.getTable(PATH_SEPARATOR+key); 111 } 112 113 114 115 private final String path; 116 private final EntryCache entryCache; 117 private final NetworkTableKeyCache absoluteKeyCache; 118 private final NetworkTableProvider provider; 119 private final NetworkTableNode node; 120 121 NetworkTable(String path, NetworkTableProvider provider) { 122 this.path = path; 123 entryCache = new EntryCache(path); 124 absoluteKeyCache = new NetworkTableKeyCache(path); 125 this.provider = provider; 126 node = provider.getNode(); 127 } 128 public String toString(){ 129 return "NetworkTable: "+path; 130 } 131 132 public boolean isConnected() { 133 return node.isConnected(); 134 } 135 136 public boolean isServer() { 137 return node.isServer(); 138 } 139 140 141 static class NetworkTableKeyCache extends StringCache{ 142 private final String path; 143 144 public NetworkTableKeyCache(String path) { 145 this.path = path; 146 } 147 148 public String calc(String key) { 149 return path + PATH_SEPARATOR + key; 150 } 151 } 152 class EntryCache { 153 private final Hashtable cache = new Hashtable(); 154 private final String path; 155 156 public EntryCache(String path) { 157 this.path = path; 158 } 159 160 public NetworkTableEntry get(final String key){ 161 NetworkTableEntry cachedValue = (NetworkTableEntry)cache.get(key); 162 if(cachedValue==null){ 163 cachedValue = node.getEntryStore().getEntry(absoluteKeyCache.get(key)); 164 if(cachedValue!=null) 165 cache.put(key, cachedValue); 166 } 167 return cachedValue; 168 } 169 } 170 171 172 private final Hashtable connectionListenerMap = new Hashtable(); 173 public void addConnectionListener(IRemoteConnectionListener listener, boolean immediateNotify) { 174 NetworkTableConnectionListenerAdapter adapter = (NetworkTableConnectionListenerAdapter)connectionListenerMap.get(listener); 175 if(adapter!=null) 176 throw new IllegalStateException("Cannot add the same listener twice"); 177 adapter = new NetworkTableConnectionListenerAdapter(this, listener); 178 connectionListenerMap.put(listener, adapter); 179 node.addConnectionListener(adapter, immediateNotify); 180 } 181 182 public void removeConnectionListener(IRemoteConnectionListener listener) { 183 NetworkTableConnectionListenerAdapter adapter = (NetworkTableConnectionListenerAdapter)connectionListenerMap.get(listener); 184 if(adapter!=null) 185 node.removeConnectionListener(adapter); 186 } 187 188 189 public void addTableListener(ITableListener listener) { 190 addTableListener(listener, false); 191 } 192 193 private final Hashtable listenerMap = new Hashtable(); 194 public void addTableListener(ITableListener listener, boolean immediateNotify) { 195 List adapters = (List)listenerMap.get(listener); 196 if(adapters==null){ 197 adapters = new List(); 198 listenerMap.put(listener, adapters); 199 } 200 NetworkTableListenerAdapter adapter = new NetworkTableListenerAdapter(path+PATH_SEPARATOR, this, listener); 201 adapters.add(adapter); 202 node.addTableListener(adapter, immediateNotify); 203 } 204 public void addTableListener(String key, ITableListener listener, boolean immediateNotify) { 205 List adapters = (List)listenerMap.get(listener); 206 if(adapters==null){ 207 adapters = new List(); 208 listenerMap.put(listener, adapters); 209 } 210 NetworkTableKeyListenerAdapter adapter = new NetworkTableKeyListenerAdapter(key, absoluteKeyCache.get(key), this, listener); 211 adapters.add(adapter); 212 node.addTableListener(adapter, immediateNotify); 213 } 214 public void addSubTableListener(final ITableListener listener) { 215 List adapters = (List)listenerMap.get(listener); 216 if(adapters==null){ 217 adapters = new List(); 218 listenerMap.put(listener, adapters); 219 } 220 NetworkTableSubListenerAdapter adapter = new NetworkTableSubListenerAdapter(path, this, listener); 221 adapters.add(adapter); 222 node.addTableListener(adapter, true); 223 } 224 225 public void removeTableListener(ITableListener listener) { 226 List adapters = (List)listenerMap.get(listener); 227 if(adapters!=null){ 228 for(int i = 0; i<adapters.size(); ++i) 229 node.removeTableListener((ITableListener) adapters.get(i)); 230 adapters.clear(); 231 } 232 } 233 234 private synchronized NetworkTableEntry getEntry(String key){ 235 return entryCache.get(key); 236 } 237 238 /** 239 * Returns the table at the specified key. If there is no table at the 240 * specified key, it will create a new table 241 * 242 * @param key 243 * the key name 244 * @return the networktable to be returned 245 */ 246 public synchronized ITable getSubTable(String key) { 247 return (NetworkTable)provider.getTable(absoluteKeyCache.get(key)); 248 } 249 250 /** 251 * Checks the table and tells if it contains the specified key 252 * 253 * @param key 254 * the key to be checked 255 */ 256 public boolean containsKey(String key) { 257 return node.containsKey(absoluteKeyCache.get(key)); 258 } 259 260 public boolean containsSubTable(String key){ 261 String subtablePrefix = absoluteKeyCache.get(key)+PATH_SEPARATOR; 262 List keys = node.getEntryStore().keys(); 263 for(int i = 0; i<keys.size(); ++i){ 264 if(((String)keys.get(i)).startsWith(subtablePrefix)) 265 return true; 266 } 267 return false; 268 } 269 270 /** 271 * Maps the specified key to the specified value in this table. The key can 272 * not be null. The value can be retrieved by calling the get method with a 273 * key that is equal to the original key. 274 * 275 * @param key 276 * the key 277 * @param value 278 * the value 279 */ 280 public void putNumber(String key, double value) { 281 putValue(key, new Double(value));//TODO cache doubles 282 } 283 284 /** 285 * Returns the key that the name maps to. 286 * 287 * @param key 288 * the key name 289 * @return the key 290 * @throws TableKeyNotDefinedException 291 * if the specified key is null 292 */ 293 public double getNumber(String key) throws TableKeyNotDefinedException { 294 return node.getDouble(absoluteKeyCache.get(key)); 295 } 296 297 /** 298 * Returns the key that the name maps to. If the key is null, it will return 299 * the default value 300 * 301 * @param key 302 * the key name 303 * @param defaultValue 304 * the default value if the key is null 305 * @return the key 306 */ 307 public double getNumber(String key, double defaultValue) { 308 try { 309 return node.getDouble(absoluteKeyCache.get(key)); 310 } catch (TableKeyNotDefinedException e) { 311 return defaultValue; 312 } 313 } 314 315 /** 316 * Maps the specified key to the specified value in this table. The key can 317 * not be null. The value can be retrieved by calling the get method with a 318 * key that is equal to the original key. 319 * 320 * @param key 321 * the key 322 * @param value 323 * the value 324 */ 325 public void putString(String key, String value) { 326 putValue(key, value); 327 } 328 329 /** 330 * Returns the key that the name maps to. 331 * 332 * @param key 333 * the key name 334 * @return the key 335 * @throws TableKeyNotDefinedException 336 * if the specified key is null 337 */ 338 public String getString(String key) throws TableKeyNotDefinedException { 339 return node.getString(absoluteKeyCache.get(key)); 340 } 341 342 /** 343 * Returns the key that the name maps to. If the key is null, it will return 344 * the default value 345 * 346 * @param key 347 * the key name 348 * @param defaultValue 349 * the default value if the key is null 350 * @return the key 351 */ 352 public String getString(String key, String defaultValue) { 353 try { 354 return node.getString(absoluteKeyCache.get(key)); 355 } catch (TableKeyNotDefinedException e) { 356 return defaultValue; 357 } 358 } 359 360 /** 361 * Maps the specified key to the specified value in this table. The key can 362 * not be null. The value can be retrieved by calling the get method with a 363 * key that is equal to the original key. 364 * 365 * @param key 366 * the key 367 * @param value 368 * the value 369 */ 370 public void putBoolean(String key, boolean value) { 371 putValue(key, value?Boolean.TRUE:Boolean.FALSE); 372 } 373 374 /** 375 * Returns the key that the name maps to. 376 * 377 * @param key 378 * the key name 379 * @return the key 380 * @throws TableKeyNotDefinedException 381 * if the specified key is null 382 */ 383 public boolean getBoolean(String key) throws TableKeyNotDefinedException { 384 return node.getBoolean(absoluteKeyCache.get(key)); 385 } 386 387 /** 388 * Returns the key that the name maps to. If the key is null, it will return 389 * the default value 390 * 391 * @param key 392 * the key name 393 * @param defaultValue 394 * the default value if the key is null 395 * @return the key 396 */ 397 public boolean getBoolean(String key, boolean defaultValue) { 398 try { 399 return node.getBoolean(absoluteKeyCache.get(key)); 400 } catch (TableKeyNotDefinedException e) { 401 return defaultValue; 402 } 403 } 404 405 406 public void retrieveValue(String key, Object externalValue) { 407 node.retrieveValue(absoluteKeyCache.get(key), externalValue); 408 } 409 410 /** 411 * Maps the specified key to the specified value in this table. The key can 412 * not be null. The value can be retrieved by calling the get method with a 413 * key that is equal to the original key. 414 * 415 * @param key the key name 416 * @param value the value to be put 417 */ 418 public void putValue(String key, Object value){ 419 NetworkTableEntry entry = entryCache.get(key); 420 if(entry!=null) 421 node.putValue(entry, value); 422 else 423 node.putValue(absoluteKeyCache.get(key), value); 424 } 425 426 /** 427 * Returns the key that the name maps to. 428 * NOTE: If the value is a double, it will return a Double object, 429 * not a primitive. To get the primitive, use getDouble 430 * 431 * @param key 432 * the key name 433 * @return the key 434 * @throws TableKeyNotDefinedException 435 * if the specified key is null 436 */ 437 public Object getValue(String key) throws TableKeyNotDefinedException { 438 return node.getValue(absoluteKeyCache.get(key)); 439 } 440 441 /** 442 * Returns the key that the name maps to. If the key is null, it will return 443 * the default value 444 * NOTE: If the value is a double, it will return a Double object, 445 * not a primitive. To get the primitive, use getDouble 446 * 447 * @param key 448 * the key name 449 * @param defaultValue 450 * the default value if the key is null 451 * @return the key 452 */ 453 public Object getValue(String key, Object defaultValue) { 454 try { 455 return node.getValue(absoluteKeyCache.get(key)); 456 } catch(TableKeyNotDefinedException e){ 457 return defaultValue; 458 } 459 } 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 /* 483 * Depricated Methods 484 */ 485 /** 486 * @deprecated 487 * Maps the specified key to the specified value in this table. 488 * The key can not be null. 489 * The value can be retrieved by calling the get method with a key that is equal to the original key. 490 * @param key the key 491 * @param value the value 492 * @throws IllegalArgumentException if key is null 493 */ 494 public void putInt(String key, int value) { 495 putNumber(key, value); 496 } 497 498 /** 499 * @deprecated 500 * Returns the value at the specified key. 501 * @param key the key 502 * @return the value 503 * @throws TableKeyNotDefinedException if there is no value mapped to by the key 504 * @throws IllegalArgumentException if the value mapped to by the key is not an int 505 * @throws IllegalArgumentException if the key is null 506 */ 507 public int getInt(String key) throws TableKeyNotDefinedException{ 508 return (int) getNumber(key); 509 } 510 511 /** 512 * @deprecated 513 * Returns the value at the specified key. 514 * @param key the key 515 * @param defaultValue the value returned if the key is undefined 516 * @return the value 517 * @throws NetworkTableKeyNotDefined if there is no value mapped to by the key 518 * @throws IllegalArgumentException if the value mapped to by the key is not an int 519 * @throws IllegalArgumentException if the key is null 520 */ 521 public int getInt(String key, int defaultValue) throws TableKeyNotDefinedException{ 522 try { 523 return (int) getNumber(key); 524 } catch (NoSuchElementException ex) { 525 return defaultValue; 526 } 527 } 528 529 /** 530 * @deprecated 531 * Maps the specified key to the specified value in this table. 532 * The key can not be null. 533 * The value can be retrieved by calling the get method with a key that is equal to the original key. 534 * @param key the key 535 * @param value the value 536 * @throws IllegalArgumentException if key is null 537 */ 538 public void putDouble(String key, double value) { 539 putNumber(key, value); 540 } 541 542 /** 543 * @deprecated 544 * Returns the value at the specified key. 545 * @param key the key 546 * @return the value 547 * @throws NoSuchEleNetworkTableKeyNotDefinedmentException if there is no value mapped to by the key 548 * @throws IllegalArgumentException if the value mapped to by the key is not a double 549 * @throws IllegalArgumentException if the key is null 550 */ 551 public double getDouble(String key) throws TableKeyNotDefinedException{ 552 return getNumber(key); 553 } 554 555 /** 556 * @deprecated 557 * Returns the value at the specified key. 558 * @param key the key 559 * @param defaultValue the value returned if the key is undefined 560 * @return the value 561 * @throws NoSuchEleNetworkTableKeyNotDefinedmentException if there is no value mapped to by the key 562 * @throws IllegalArgumentException if the value mapped to by the key is not a double 563 * @throws IllegalArgumentException if the key is null 564 */ 565 public double getDouble(String key, double defaultValue) { 566 return getNumber(key, defaultValue); 567 } 568}