001 package edu.nrao.sss.model.resource; 002 003 import java.awt.event.ActionListener; 004 import java.util.ArrayList; 005 import java.util.HashMap; 006 import java.util.List; 007 import java.util.Map; 008 009 import javax.xml.bind.annotation.XmlElementRef; 010 import javax.xml.bind.annotation.XmlElementRefs; 011 import javax.xml.bind.annotation.XmlElementWrapper; 012 import javax.xml.bind.annotation.XmlTransient; 013 014 import edu.nrao.sss.electronics.DigitalSignal; 015 import edu.nrao.sss.model.resource.evla.EvlaWidarConfiguration; 016 import edu.nrao.sss.model.resource.vla.VlaConfiguration; 017 import edu.nrao.sss.util.Identifiable; 018 019 import org.apache.log4j.Logger; 020 021 /** 022 * Configuration information for a correlator of a radio telescope. 023 * 024 * <h3 style="font-variant:small-caps"><u>How To Use This Class</u></h3> 025 * <div style="margin-left:1.5em; margin-right:2em; text-align:justify;"> 026 * <p> 027 * <i><b>Getting an Instance</b></i><br/> 028 * Clients are discouraged from creating new instances of this type by using the 029 * constructors of concrete subclasses. The preferred way to get an instance of 030 * this type is to use the static factory method, 031 * {@link #makeFor(CorrelatorName, AntennaElectronics)}. 032 * The static method {@link #getSupportedCorrelators()} lists the 033 * correlator supported by this configuration class.</p> 034 * <p> 035 * <i><b>Working with Basebands and Baseband Pairs</b></i><br/> 036 * This configuration now has only one method for fetching basebands: 037 * {@link #getBasebands()}. The method <tt>getBasebandPairs()</tt> has been 038 * eliminated, but the {@link CorrelatorBaseband} now has methods to determine 039 * whether or not it is part of pair and, if so, who its parter is. 040 * The basebands held by this configuration were 041 * originally constructed based on the output signals of the 042 * {@link AntennaElectronics} provided to the factory method.</p> 043 * <p> 044 * <i><b>Listening for Changes</b></i><br/> 045 * This configuration gives clients the ability to listen for changes in 046 * the <i>collection</i> of basebands in this configuration. Clients 047 * may register by using the 048 * {@link #addBasebandCollectionListener(BasebandCollectionListener) 049 * addBasebandCollectionListener} method. 050 * Registered users will be notified when a baseband(s) is added to the 051 * configuration, removed from the configuration, or both at once. 052 * Note that a listener's 053 * {@link BasebandCollectionListener#basebandCollectionChanged(BasebandCollectionEvent) 054 * basebandCollectionChanged} method is called only when the collection itself 055 * changes, <i>not</i> when properties of the contained basebands change.</p> 056 * </div> 057 * <p> 058 * <b>Version Info:</b> 059 * <table style="margin-left:2em"> 060 * <tr><td>$Revision: 2287 $</td></tr> 061 * <tr><td>$Date: 2009-05-07 13:54:58 -0600 (Thu, 07 May 2009) $</td></tr> 062 * <tr><td>$Author: dharland $ (last person to modify)</td></tr> 063 * </table></p> 064 * 065 * @author David M. Harland 066 * @since 2008-02-04 067 */ 068 public abstract class CorrelatorConfiguration 069 implements TelescopeBackend, ActionListener 070 { 071 private static final Logger LOG = Logger.getLogger(CorrelatorConfiguration.class); 072 073 private Long id; //A unique identifier for the persistence layer. 074 075 /** The basebands of this configuration in a map keyed by name. */ 076 //protected Map<String, CorrelatorBaseband> activeBBs; 077 078 /** The basebands of this configuration. */ 079 protected List<CorrelatorBasebandAbs> activeBBs; 080 081 /** 082 * Basebands that had been, but are not currently, active. 083 * Subclasses do not have to work with this variable. 084 * The intention behind it is to help with a single-level 085 * "redo" feature. The targeted use case goes something like this: 086 * <ol> 087 * <li>A user has set up the antenna electronics that feeds this 088 * configuration in such a way that eight basebands are produced.</li> 089 * <li>The user completely configures all basebands, including 090 * breaking them down into subbands.</li> 091 * <li>The user then either intentionally or accidentally modifies 092 * the antenna electronics in such a way that two of the fully 093 * configured basebands are replaced by two new basebands.</li> 094 * <li>The user puts the antenna electronics back into its 095 * original state. This means that the original eight basebands 096 * are again active.</li> 097 * </ol> 098 * This variable can be used to restore all the work the user had 099 * done on the disappearing basebands when they reappear. 100 */ 101 protected Map<String, CorrelatorBasebandAbs> inactiveBBs; 102 103 /** The provider of digital signals for the basebands of this configuration.*/ 104 protected AntennaElectronics signalSource; 105 106 /** Objects interested in changes to our collection of basebands. */ 107 protected List<BasebandCollectionListener> bbListeners; 108 109 /** 110 * Creates and returns a new configuration for the given correlator that is 111 * initialized from {@code signalSrc}. 112 * 113 * @param correlator 114 * the name of a known correlator. 115 * 116 * @param signalSrc 117 * the antenna electronics that provide the input signals for the basebands 118 * of this configuration. 119 * 120 * @return 121 * a new configuration for the given correlator whose basebands are 122 * initialized based on the given antenna electronics. 123 * 124 * @throws IllegalArgumentException 125 * if {@code correlator} is not one of the supported types. 126 */ 127 public static CorrelatorConfiguration makeFor(CorrelatorName correlator, 128 AntennaElectronics signalSrc) 129 { 130 switch(correlator) 131 { 132 case VLA: return new VlaConfiguration(signalSrc); 133 case WIDAR: return new EvlaWidarConfiguration(signalSrc); 134 135 default: 136 throw new IllegalArgumentException("The " + correlator + 137 " correlator is not yet supported."); 138 } 139 } 140 141 /** 142 * Returns a list of the correlators that are supported by the 143 * {@link #makeFor(CorrelatorName, AntennaElectronics) factory 144 * method} of this class. 145 * 146 * @return 147 * the correlators for which this class can construct configurations. 148 */ 149 public static List<CorrelatorName> getSupportedCorrelators() 150 { 151 List<CorrelatorName> list = new ArrayList<CorrelatorName>(); 152 153 list.add(CorrelatorName.WIDAR); 154 list.add(CorrelatorName.VLA); 155 156 return list; 157 } 158 159 /** 160 * Constructs a new configuration that is initialized from {@code signalSrc}. 161 * 162 * @param signalSrc 163 * the antenna electronics that provide the input signals for the basebands 164 * of this configuration. 165 * 166 * @throws IllegalArgumentException 167 * if {@code signalSrc} is <i>null</i>. 168 */ 169 protected CorrelatorConfiguration(AntennaElectronics signalSrc) 170 { 171 if (signalSrc == null) 172 throw new IllegalArgumentException("You may not create a new correlator" + 173 " configuration with NULL AntennaElectronics."); 174 175 init(signalSrc); 176 } 177 178 //This is here only for persistence mechanisms 179 protected CorrelatorConfiguration() 180 { 181 init(null); 182 } 183 184 //All constructors should lead to a call to this method 185 private void init(AntennaElectronics signalSrc) 186 { 187 id = Identifiable.UNIDENTIFIED; 188 189 activeBBs = new ArrayList<CorrelatorBasebandAbs>(); 190 inactiveBBs = new HashMap<String, CorrelatorBasebandAbs>(); 191 bbListeners = new ArrayList<BasebandCollectionListener>(); 192 193 setSigSrc(signalSrc); 194 } 195 196 //============================================================================ 197 // IDENTIFICATION 198 //============================================================================ 199 200 /* (non-Javadoc) 201 * @see edu.nrao.sss.util.Identifiable#getId() 202 */ 203 public Long getId() { return id; } 204 205 @XmlTransient public void setId(Long id) { this.id = id; } 206 207 /* (non-Javadoc) 208 * @see edu.nrao.sss.model.resource.TelescopeBackend#clearId() 209 */ 210 public void clearId() 211 { 212 id = Identifiable.UNIDENTIFIED; 213 214 for (CorrelatorBaseband bb : activeBBs) 215 bb.clearId(); 216 217 //This is probably not necessary (not persisting inactiveBBs) 218 for (CorrelatorBaseband bb : inactiveBBs.values()) 219 bb.clearId(); 220 } 221 222 //============================================================================ 223 // SOURCE OF SIGNAL 224 //============================================================================ 225 226 //Used by constructor and by public method 227 private void setSigSrc(AntennaElectronics newSource) 228 { 229 //Quick exit if attempt to set to what we already have 230 if (newSource == signalSource) 231 return; 232 233 //Stop listening to old signal source 234 if (signalSource != null) 235 signalSource.removeExecutionListener(this); 236 237 //Save the new source, even if it's null 238 signalSource = newSource; 239 240 if (signalSource != null) 241 { 242 //When the antenna electronics is executed, we will be notified. 243 //That execution could cause us to change the number of active 244 //basebands, or just retune where the current basebands begin. 245 signalSource.addExecutionListener(this); 246 247 //Synchronize this configuration with the current state of the 248 //antenna electronics 249 //NOTE from DMH 2008-05-21 250 // I deactivated this call so that the pathway that starts at either 251 // the database or an XML file, winds its way through 252 // Resource.connectBackendsToAntElec(), and then finds itself here 253 // does not blow away the backend data we just fetched from the DB 254 // or XML. 255 // The reason it gets blown away is that the antenna electronics 256 // have not been executed and have null signals. The act of syncing 257 // the front end w/ the backend results in null signal in the back. 258 // There are other ways to deal w/ this. One would be to execute 259 // the electronics when fetching from DB/XML. We'd need to beware, 260 // though, of signals produced by the execute that don't match the 261 // stored backend signals. Another would be to go into the 262 // actionPerformed methods of the backends and not replace the signals 263 // under some conditions (eg, prior sig src was null and new one is not, 264 // action.equals("New source"), or some such). 265 // 266 // I'm a little worried that fixing the use-case at hand may muck up 267 // others, hence the deactivation -- rather than removal -- of the line 268 // below and this note. If you find you want to RE-activate the line 269 // below, be aware of the DB / XML issues. 270 //actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, 271 // "New source")); 272 } 273 } 274 275 /** 276 * Sets the source of input signals for this configuration. 277 * Most clients will not need to use this method, since it is expected that 278 * they created this configuration with a valid signal source in the first 279 * place. 280 * 281 * @param newSource 282 * a new source of input signals for this configuration. 283 * A value of <i>null</i> is accepted here. 284 */ 285 @XmlTransient 286 public void setSignalSource(AntennaElectronics newSource) 287 { 288 setSigSrc(newSource); 289 } 290 291 /** 292 * Returns the source of input signals for this configuration, if any. 293 * If no signal source has been provided, the returned value will be 294 * <i>null</i>. 295 * 296 * @return the source of input signals for this configuration. 297 */ 298 public AntennaElectronics getSignalSource() 299 { 300 return signalSource; 301 } 302 303 //============================================================================ 304 // BASEBANDS 305 //============================================================================ 306 307 /** 308 * Allows concrete subclasses to be in charge of the ordering of the basebands 309 * returned by {@link #getBasebands()}. 310 * This default implementation returns a copy of the <tt>activeBBs</tt> list 311 * without any reordering 312 */ 313 protected List<CorrelatorBaseband> getOrderedBasebands() 314 { 315 return new ArrayList<CorrelatorBaseband>(activeBBs); 316 } 317 318 /** 319 * Creates and initializes the active basebands for this configuration 320 * based on the antenna electronics that were sent to the constructor. 321 * This method is called from the constructor after the 322 * <tt>signalSource</tt> variable was set. 323 * 324 protected void initializeBasebands() 325 { 326 activeBBs.clear(); 327 inactiveBBs.clear(); 328 329 //Create new basebands from the input signals and save as active BBs 330 for (List<DigitalSignal> inputPair : signalSource.getSignalPairs()) 331 { 332 CorrelatorBaseband bb0 = makeBasebandFrom(inputPair.get(0)); 333 CorrelatorBaseband bb1 = makeBasebandFrom(inputPair.get(1)); 334 335 if (bb0 != null) 336 activeBBs.put(bb0.getName(), bb0); 337 338 if (bb1 != null) 339 activeBBs.put(bb1.getName(), bb0); 340 } 341 }*/ 342 343 public BackendType getType() { return BackendType.CORRELATOR;} 344 345 /** 346 * Creates a new baseband from the given digital signal. 347 * Subclasses of this one decide which implementation of a 348 * <tt>CorrelatorBaseband</tt> to construct. 349 * <p> 350 * This method is expected to produce a <tt>CorrelatorBaseband</tt> 351 * whose {@link CorrelatorBaseband#isSinglet()} property is 352 * <i>true</i>.</p> 353 * 354 * @param ds 355 * the digital signal that serves as the signal source for the 356 * newly created baseband. 357 * 358 * @return 359 * a new baseband, or <i>null</i> if {@code ds} is <i>null</i>. 360 */ 361 protected abstract CorrelatorBaseband makeBasebandFrom(DigitalSignal ds); 362 363 /** 364 * Creates a new baseband from the given digital signals. 365 * Subclasses of this one decide which implementation of a 366 * <tt>CorrelatorBaseband</tt> to construct. 367 * <p> 368 * This method is expected to produce a <tt>CorrelatorBaseband</tt> 369 * whose {@link CorrelatorBaseband#isPair()} property is 370 * <i>true</i>.</p> 371 * 372 * @param ds1 373 * one of two digital signals that serve as the signal sources for the 374 * newly created baseband. 375 * 376 * @param ds2 377 * one of two digital signals that serve as the signal sources for the 378 * newly created baseband. 379 * 380 * @return 381 * a new baseband, or <i>null</i> if either {@code ds1} or {@code ds2} 382 * is <i>null</i>. 383 */ 384 protected abstract CorrelatorBaseband makeBasebandFrom(DigitalSignal ds1, 385 DigitalSignal ds2); 386 387 /** 388 * Returns the basebands of this correlator configuration. 389 * <p> 390 * While the basebands in the returned list are the actual basebands 391 * held internally by this configuration, the list itself is a new 392 * list created at the time this method is called and not referenced 393 * by this configuration. The returned list may be empty, but will 394 * never be <i>null</i>.</p> 395 * 396 * @return the basebands of this correlator configuration. 397 */ 398 public List<CorrelatorBaseband> getBasebands() 399 { 400 return getOrderedBasebands(); 401 } 402 403 //---------------------------------------------------------------------------- 404 // Persistence Helpers 405 //---------------------------------------------------------------------------- 406 407 //For the list of baseband pairs we have one set of logic for Hibernate & 408 //one for JAXB. In XML we're making basebandPairs an optional element, but 409 //for Hibernate it cannot be null (or else Hibernate throws an exception). 410 @XmlElementWrapper(name="basebands") 411 @XmlElementRefs 412 ( 413 { 414 @XmlElementRef(type=edu.nrao.sss.model.resource.evla.WidarBasebandSinglet.class), 415 @XmlElementRef(type=edu.nrao.sss.model.resource.evla.WidarBasebandPair.class), 416 @XmlElementRef(type=edu.nrao.sss.model.resource.vla.VlaBasebandPair.class) 417 } 418 ) 419 @SuppressWarnings("unused") 420 private void setXmlBasebandList(CorrelatorBasebandAbs[] replacementList) 421 { 422 if (replacementList == null) 423 { 424 activeBBs = new ArrayList<CorrelatorBasebandAbs>(); 425 } 426 else 427 { 428 ArrayList<CorrelatorBasebandAbs> newList = 429 new ArrayList<CorrelatorBasebandAbs>(); 430 431 for (CorrelatorBasebandAbs cbp : replacementList) 432 newList.add(cbp); 433 434 activeBBs = newList; 435 } 436 437 createdBasebandsFromPersistentStore(); 438 } 439 @SuppressWarnings("unused") 440 private CorrelatorBasebandAbs[] getXmlBasebandList() 441 { 442 return activeBBs.size() == 0 ? 443 null : activeBBs.toArray(new CorrelatorBasebandAbs[activeBBs.size()]); 444 } 445 446 @SuppressWarnings("unused") //Used by Hibernate 447 private void setBasebandList(List<CorrelatorBasebandAbs> replacementList) 448 { 449 LOG.warn("Setting baseband list. Size = "+(replacementList==null?0:replacementList.size())); 450 451 activeBBs = 452 replacementList == null ? new ArrayList<CorrelatorBasebandAbs>() 453 : replacementList; 454 455 createdBasebandsFromPersistentStore(); 456 } 457 @SuppressWarnings("unused") //Used by Hibernate 458 private List<CorrelatorBasebandAbs> getBasebandList() 459 { 460 LOG.warn("Getting baseband list; activeBBs.size = "+activeBBs.size()); 461 return activeBBs; 462 } 463 464 /** 465 * Called after basebands were created from a persistent store, 466 * such as a database or XMl file. This method is here so that 467 * subclasses may override it. This default implementation does nothing. 468 */ 469 protected void createdBasebandsFromPersistentStore() 470 { 471 //Default do-nothing implementation 472 } 473 474 //============================================================================ 475 // LISTENERS 476 //============================================================================ 477 478 /** 479 * Registers a new listener that will be notified whenever the collection 480 * of basebands in this configuration is changed. Note that "changed" means 481 * that there are new basebands in the collection, that some basebands 482 * formerly in the collection no longer are, or both of the preceding. 483 * For the purpose of the <tt>BasebandCollectionListener</tt>, a change to 484 * a baseband already in the collection is <i>not</i> considered an event. 485 * Only a modification of the collection itself causes notification. 486 * 487 * @param newListener 488 * an object that will be notified when the collection of basebands held 489 * by this configuration changes. A value of <i>null</i> will be ignored. 490 */ 491 public void addBasebandCollectionListener(BasebandCollectionListener newListener) 492 { 493 if (newListener != null) 494 bbListeners.add(newListener); 495 } 496 497 /** 498 * Removes {@code formerListener} from this configuration's list of baseband 499 * collection listeners. If {@code formerListener} is not currently a 500 * registered baseband collection listener, this method does nothing. 501 * 502 * @param formerListener 503 * an object that no longer wishes to be notified about changes in the 504 * collection of basebands held by this configuration. 505 */ 506 public void removeBasebandCollectionListener(BasebandCollectionListener formerListener) 507 { 508 bbListeners.remove(formerListener); 509 } 510 511 //============================================================================ 512 // 513 //============================================================================ 514 515 /** 516 * Returns a copy of this configuration. 517 * The returned object is a deep copy of this one, with the following 518 * exceptions: 519 * <ol> 520 * <li>Both configurations are using the same signal source.</li> 521 * <li>While each configuration has its own list of listeners, 522 * the same listeners are present in each list.</li> 523 * </ol> 524 * <p> 525 * If anything goes wrong during the cloning procedure, 526 * a {@code RuntimeException} will be thrown.</p> 527 */ 528 @Override 529 public CorrelatorConfiguration clone() 530 { 531 CorrelatorConfiguration clone = null; 532 533 try 534 { 535 //This line takes care of the primitive & immutable fields properly 536 clone = (CorrelatorConfiguration)super.clone(); 537 538 //We do NOT want the clone to have the same ID as the original. 539 //The ID is here for the persistence layer; it is in charge of 540 //setting IDs. To help it, we put the clone's ID in the uninitialized 541 //state. 542 clone.id = Identifiable.UNIDENTIFIED; 543 544 //Clone the collection AND the elements 545 clone.activeBBs = new ArrayList<CorrelatorBasebandAbs>(); 546 for (CorrelatorBasebandAbs thisBB : this.activeBBs) 547 clone.activeBBs.add(thisBB.clone()); 548 549 //Clone the collection AND the elements 550 clone.inactiveBBs = new HashMap<String, CorrelatorBasebandAbs>(); 551 for (String bbName : this.inactiveBBs.keySet()) 552 clone.inactiveBBs.put(bbName, this.inactiveBBs.get(bbName).clone()); 553 554 //We clone the listener collection, but NOT the listeners themselves 555 clone.bbListeners = 556 new ArrayList<BasebandCollectionListener>(this.bbListeners); 557 } 558 catch (Exception ex) 559 { 560 throw new RuntimeException(ex); 561 } 562 563 return clone; 564 } 565 566 /** Returns <i>true</i> if {@code o} is equal to this configuration. */ 567 @Override 568 public boolean equals(Object o) 569 { 570 //Quick exit if o is this 571 if (o == this) 572 return true; 573 574 //Quick exit if o is null 575 if (o == null) 576 return false; 577 578 //Quick exit if classes are different 579 if (!o.getClass().equals(this.getClass())) 580 return false; 581 582 //A safe cast if we got this far 583 CorrelatorConfiguration other = (CorrelatorConfiguration)o; 584 585 //We're intentionally not comparing: 586 //1. inactiveBBs because 587 // a. we don't persist these 588 // b. we don't care about them in this context 589 //2. the listeners 590 //3. the signal source 591 return other.activeBBs.equals(this.activeBBs); 592 } 593 594 /** Returns a hash code value for this configuration. */ 595 @Override 596 public int hashCode() 597 { 598 //Taken from the Effective Java book by Joshua Bloch. 599 //The constants 17 & 37 are arbitrary & carry no meaning. 600 int result = 17; 601 602 //You MUST keep this method in sync w/ the equals method 603 604 result = 37 * result + activeBBs.hashCode(); 605 606 return result; 607 } 608 }