001 package edu.nrao.sss.electronics; 002 003 import java.util.ArrayList; 004 import java.util.HashMap; 005 import java.util.List; 006 import java.util.Map; 007 008 /** 009 * A device that can connect one of its inputs to one of its outputs 010 * and pass signals through that connection. Only one input may be 011 * connected to an output, and only one output may be connected to 012 * an input. The remaining inputs and outputs (collectively, "poles") 013 * are not connected to any outputs or inputs, respectively. 014 * <p> 015 * For simultaneously connecting a single input to many outputs 016 * see the {@link SignalManifold} class. 017 * See also the {@link SignalTransferSwitch} for a variation of a 018 * switch that connects <tt>N</tt> inputs to <tt>N</tt> outputs simultaneously, 019 * with each pole connected to exactly one other, complementary, pole.</p> 020 * <p> 021 * Signals pass through switches without being altered.</p> 022 * <p> 023 * The following examples were all taken from the EVLA Antenna Block 024 * Diagram:</p> 025 * <table frame="border" align="center"> 026 * <tr> 027 * <td align="center"><img src="doc-files/switch4in1out.png"/></td> 028 * <td align="center"><img src="doc-files/switch2in1out.png"/></td> 029 * <td align="center"><img src="doc-files/switch1in4out.png"/></td> 030 * <td align="center"><img src="doc-files/switch1in2out.png"/></td> 031 * </tr> 032 * <tr> 033 * <td align="center"><i>4 inputs, 1 output</i></td> 034 * <td align="center"><i>2 inputs, 1 output<br/> 035 * (assuming left-to-right flow)</i></td> 036 * <td align="center"><i>1 input, 4 outputs</i></td> 037 * <td align="center"><i>1 input, 2 outputs<br/> 038 * (assuming left-to-right flow)</i></td> 039 * </tr> 040 * </table> 041 * <p> 042 * Note that indexing for input and output poles follows the java convention 043 * of starting with zero, which is different than what is displayed in the 044 * diagrams above. In addition to referencing poles by index, clients 045 * may name the input and output poles and then select them by name. 046 * If a client names two poles of the same kind (input or output) identically, 047 * the pole returned by the select-by-name methods is indeterminate.</p> 048 * <p> 049 * <b>Version Info:</b> 050 * <table style="margin-left:2em"> 051 * <tr><td>$Revision: 1198 $</td></tr> 052 * <tr><td>$Date: 2008-04-02 18:59:31 -0600 (Wed, 02 Apr 2008) $</td></tr> 053 * <tr><td>$Author: dharland $ (last person to modify)</td></tr> 054 * </table></p> 055 * 056 * @author David M. Harland 057 * @since 2007-10-24 058 */ 059 public class SignalSwitch 060 implements SignalProcessor, SignalSource 061 { 062 private static final String DEFAULT_NAME = "[unnamed-switch]"; 063 064 private String name; 065 066 private List<SignalPipe> inputPipes; 067 private List<SignalPipe> outputPipes; 068 069 private Map<String, SignalPipe> inputPipeNameMap; 070 private Map<String, SignalPipe> outputPipeNameMap; 071 072 private SignalPipe selectedInput; 073 private SignalPipe selectedOutput; 074 075 //============================================================================ 076 // OBJECT CREATION 077 //============================================================================ 078 079 private SignalSwitch(String switchName, int numInputs, int numOutputs) 080 { 081 name = (switchName == null) ? DEFAULT_NAME : switchName; 082 083 inputPipes = new ArrayList<SignalPipe>(numInputs); 084 outputPipes = new ArrayList<SignalPipe>(numOutputs); 085 086 for (int p=0; p < numInputs; p++) 087 inputPipes.add(SignalPipe.makeAndWeldOutputTo(this)); 088 089 for (int p=0; p < numOutputs; p++) 090 outputPipes.add(SignalPipe.makeAndWeldInputTo(this)); 091 092 inputPipeNameMap = new HashMap<String, SignalPipe>(); 093 outputPipeNameMap = new HashMap<String, SignalPipe>(); 094 095 selectedInput = inputPipes.get(0); 096 selectedOutput = outputPipes.get(0); 097 098 initPoleNames(numInputs, numOutputs); 099 } 100 101 /** 102 * Creates and returns a new switch with a single output pole and 103 * the given number of input poles. 104 * <p> 105 * Up to {@code numberOfInputPoles} sources may be hooked to this 106 * switch, but exactly one of them may be selected at any one time. 107 * Initially, the 0<sup>th</sup> input is the selected input.</p> 108 * 109 * @param deviceName 110 * an optional name for this switch. If this value is <i>null</i> 111 * a default name will be used in its place. 112 * 113 * @param numberOfInputPoles 114 * the number of connections available for signal sources. 115 * 116 * @return a new switch with a single output and the given number 117 * of inputs. 118 */ 119 public static SignalSwitch makeMultiInputSwitch(String deviceName, 120 int numberOfInputPoles) 121 { 122 return new SignalSwitch(deviceName, numberOfInputPoles, 1); 123 } 124 125 /** 126 * Creates and returns a new switch with a single input pole and 127 * the given number of output poles. 128 * <p> 129 * Up to {@code numberOfOutputPoles} signal processors may be hooked to this 130 * switch, but exactly one of them may be selected at any one time. 131 * Initially, the 0<sup>th</sup> output is the selected output.</p> 132 * 133 * @param deviceName 134 * an optional name for this switch. If this value is <i>null</i> 135 * a default name will be used in its place. 136 * 137 * @param numberOfOutputPoles 138 * the number of connections available for signal processors. 139 * 140 * @return a new switch with a single input and the given number 141 * of outputs. 142 */ 143 public static SignalSwitch makeMultiOutputSwitch(String deviceName, 144 int numberOfOutputPoles) 145 { 146 return new SignalSwitch(deviceName, 1, numberOfOutputPoles); 147 } 148 149 //============================================================================ 150 // 151 //============================================================================ 152 153 /** 154 * Returns the name of this switch. 155 * @return the name of this switch. 156 */ 157 public String getName() 158 { 159 return name; 160 } 161 162 //============================================================================ 163 // INPUT AND OUTPUT POLES 164 //============================================================================ 165 166 /** 167 * Initializes the names of the input and output poles for the constructor. 168 */ 169 private void initPoleNames(int numInputs, int numOutputs) 170 { 171 //Input poles 172 String[] poleNames = new String[numInputs]; 173 174 for (int in=0; in < numInputs; in++) 175 poleNames[in] = Integer.toString(in); 176 177 nameInputs(poleNames); 178 179 //Output poles 180 poleNames = new String[numOutputs]; 181 182 for (int out=0; out < numOutputs; out++) 183 poleNames[out] = Integer.toString(out); 184 185 nameOutputs(poleNames); 186 } 187 188 /** 189 * Returns the number of input poles on this switch. 190 * @return the number of input poles on this switch. 191 */ 192 public int getNumberOfInputPoles() 193 { 194 return inputPipes.size(); 195 } 196 197 /** 198 * Returns the number of output poles on this switch. 199 * @return the number of output poles on this switch. 200 */ 201 public int getNumberOfOutputPoles() 202 { 203 return outputPipes.size(); 204 } 205 206 /** 207 * Returns <i>true</i> if this switch has an input with the given name. 208 * 209 * @param poleName 210 * the potential name of an input pole of this switch. 211 * 212 * @return <i>true</i> if this switch has an input with the given name. 213 */ 214 public boolean hasInputNamed(String poleName) 215 { 216 return inputPipeNameMap.containsKey(poleName); 217 } 218 219 /** 220 * Returns <i>true</i> if this switch has an output with the given name. 221 * 222 * @param poleName 223 * the potential name of an output pole of this switch. 224 * 225 * @return <i>true</i> if this switch has an output with the given name. 226 */ 227 public boolean hasOutputNamed(String poleName) 228 { 229 return outputPipeNameMap.containsKey(poleName); 230 } 231 232 /** 233 * Assigns names to the input poles of this switch. 234 * The names are assigned in the order presented, beginning with the 235 * pole at index zero. 236 * <p> 237 * Note: this method does not check the number of names submitted 238 * against the number of input poles.</p> 239 * 240 * @param poleNames 241 * new names for the input poles of this switch. 242 */ 243 public void nameInputs(String... poleNames) 244 { 245 int count = Math.min(poleNames.length, inputPipes.size()); 246 247 for (int i=0; i < count; i++) 248 { 249 String poleName = poleNames[i]; 250 SignalPipe input = inputPipes.get(i); 251 inputPipeNameMap.put(poleName, input); 252 input.setName(poleName); 253 } 254 } 255 256 /** 257 * Assigns names to the output poles of this switch. 258 * The names are assigned in the order presented, beginning with the 259 * pole at index zero. 260 * <p> 261 * Note: this method does not check the number of names submitted 262 * against the number of output poles.</p> 263 * 264 * @param poleNames 265 * new names for the output poles of this switch. 266 */ 267 public void nameOutputs(String... poleNames) 268 { 269 int count = Math.min(poleNames.length, outputPipes.size()); 270 271 for (int i=0; i < count; i++) 272 { 273 String poleName = poleNames[i]; 274 SignalPipe output = outputPipes.get(i); 275 outputPipeNameMap.put(poleName, output); 276 output.setName(poleName); 277 } 278 } 279 280 /** 281 * Returns the names of the input poles of this switch. 282 * <p> 283 * The names are ordered according to the numerical order of 284 * the poles that they represent. For example, if the returned 285 * list is "Inky", "Blinky", "Pinky", and "Clyde", then the 286 * pole at index zero for this switch is named "Inky" and the 287 * pole at index three is "Clyde".</p> 288 * 289 * @return the names of the input poles of this switch. 290 */ 291 public List<String> getNamesOfInputPoles() 292 { 293 List<String> names = new ArrayList<String>(); 294 295 for (SignalPipe inputPipe : inputPipes) 296 names.add(inputPipe.getName()); 297 298 return names; 299 } 300 301 /** 302 * Returns the names of the output poles of this switch. 303 * <p> 304 * The names are ordered according to the numerical order of 305 * the poles that they represent. For example, if the returned 306 * list is "Larry", "Curly", "Moe", "Shemp", and "Curly Joe", 307 * then the pole at index one for this switch is named "Curly" 308 * and the pole at index two is "Moe".</p> 309 * 310 * @return the names of the output poles of this switch. 311 */ 312 public List<String> getNamesOfOutputPoles() 313 { 314 List<String> names = new ArrayList<String>(); 315 316 for (SignalPipe outputPipe : outputPipes) 317 names.add(outputPipe.getName()); 318 319 return names; 320 } 321 322 /** 323 * Sets this switch so that it uses the input pole with the given name. 324 * The selected input pole is returned. 325 * 326 * @param poleName 327 * the potential name of an input pole of this switch. 328 * 329 * @return 330 * the selected input. 331 * 332 * @throws IllegalArgumentException 333 * if this switch has no input pole with the given name. 334 */ 335 public SignalPipe selectInput(String poleName) 336 { 337 SignalPipe input = inputPipeNameMap.get(poleName); 338 339 if (input == null) 340 { 341 throw new IllegalArgumentException("Switch " + name + 342 " has no input pole named " + poleName + 343 ". The names of this switch's input poles are: " + 344 inputPipeNameMap.keySet()); 345 } 346 else 347 { 348 selectedInput = input; 349 } 350 351 return input; 352 } 353 354 /** 355 * Sets this switch so that it uses the input pole at the given index. 356 * Indexing begins with zero. The selected input pole is returned. 357 * 358 * @param inputPole 359 * the index of the input pole to be connected to this switch's 360 * selected output. 361 * 362 * @return 363 * the selected input. 364 * 365 * @throws IndexOutOfBoundsException 366 * if {@code inputPole} is < 0 or >= the number of input poles. 367 */ 368 public SignalPipe selectInput(int inputPole) 369 { 370 selectedInput = inputPipes.get(inputPole); 371 372 return selectedInput; 373 } 374 375 /** 376 * Sets this switch so that it uses the output pole with the given name. 377 * The selected output pole is returned. 378 * 379 * @param poleName 380 * the potential name of an output pole of this switch. 381 * 382 * @return 383 * the selected output. 384 * 385 * @throws IllegalArgumentException 386 * if this switch has no output pole with the given name. 387 */ 388 public SignalPipe selectOutput(String poleName) 389 { 390 SignalPipe output = outputPipeNameMap.get(poleName); 391 392 if (output == null) 393 { 394 throw new IllegalArgumentException("Switch " + name + 395 " has no output pole named " + poleName + 396 ". The names of this switch's output poles are: " + 397 outputPipeNameMap.keySet()); 398 } 399 else 400 { 401 selectedOutput = output; 402 } 403 404 return output; 405 } 406 407 /** 408 * Sets this switch so that it uses the output pole at the given index. 409 * Indexing begins with zero. The selected output pole is returned. 410 * 411 * @param outputPole 412 * the index of the output pole to be connected to this switch's 413 * selected input. 414 * 415 * @return 416 * the selected output. 417 * 418 * @throws IndexOutOfBoundsException 419 * if {@code outputPole} is < 0 or >= the number of output poles. 420 */ 421 public SignalPipe selectOutput(int outputPole) 422 { 423 selectedOutput = outputPipes.get(outputPole); 424 425 return selectedOutput; 426 } 427 428 /** 429 * Returns the currently selected input pole. 430 * @return the currently selected input pole. 431 */ 432 public SignalPipe getSelectedInput() 433 { 434 return selectedInput; 435 } 436 437 /** 438 * Returns the currently selected output pole. 439 * @return the currently selected output pole. 440 */ 441 public SignalPipe getSelectedOutput() 442 { 443 return selectedOutput; 444 } 445 446 /** 447 * Returns the {@code index}<sup>th</sup> input pipe of this device. 448 * A typical usage pattern for this method is:<pre> 449 * myFilter.getInputPipe(p).connectInputTo(mySource);</pre> 450 * <p> 451 * Indexing begins at zero.</p> 452 * 453 * @return the {@code index}<sup>th</sup> input pipe of this device. 454 */ 455 public SignalPipe getInputPipe(int index) 456 { 457 return inputPipes.get(index); 458 } 459 460 /** 461 * Returns the {@code index}<sup>th</sup> output pipe of this device. 462 * A typical usage pattern for this method is:<pre> 463 * myFilter.getOutputPipe(p).connectOutputTo(myProcessor);</pre> 464 * <p> 465 * Indexing begins at zero.</p> 466 * 467 * @return the {@code index}<sup>th</sup> output pipe of this device. 468 */ 469 public SignalPipe getOutputPipe(int index) 470 { 471 return outputPipes.get(index); 472 } 473 474 /** 475 * Returns the index of the currently selected input pole. 476 * Indexing begins at zero. 477 * @return the index of the currently selected input pole. 478 */ 479 public int getIndexOfSelectedInput() 480 { 481 return inputPipes.indexOf(selectedInput); 482 } 483 484 /** 485 * Returns the index of the currently selected output pole. 486 * Indexing begins at zero. 487 * @return the index of the currently selected output pole. 488 */ 489 public int getIndexOfSelectedOutput() 490 { 491 return outputPipes.indexOf(selectedOutput); 492 } 493 494 /** 495 * Returns the name of the currently selected input pole. 496 * @return the name of the currently selected input pole. 497 */ 498 public String getNameOfSelectedInput() 499 { 500 return selectedInput.getName(); 501 } 502 503 /** 504 * Returns the name of the currently selected output pole. 505 * @return the name of the currently selected output pole. 506 */ 507 public String getNameOfSelectedOutput() 508 { 509 return selectedOutput.getName(); 510 } 511 512 //============================================================================ 513 // INTERFACE SignalSource 514 //============================================================================ 515 516 /** 517 * Returns the signal provided by the currently selected input pipe of this 518 * device. 519 * If nothing is connected to that input pipe, or if the connected 520 * source has a <i>null</i> signal, <i>null</i> will be returned. 521 */ 522 public Signal getSignal() 523 { 524 //Append the name of this switch to the signal's device path 525 Signal signal = selectedInput.getSignal(); 526 527 if (signal != null && !signal.getDevicePath().endsWith(name)) 528 signal.appendToDevicePath(name); 529 530 return signal; 531 } 532 533 public Signal getSignal(SignalPipe outputPipe) 534 { 535 return outputPipe == selectedOutput ? getSignal() : null; 536 } 537 538 //============================================================================ 539 // INTERFACE SignalProcessor 540 //============================================================================ 541 542 /** 543 * Executes the currently selected output pipe of this device. 544 */ 545 public void execute() 546 { 547 selectedOutput.execute(); 548 } 549 550 public void executeUpTo(SignalProcessor firstUnexecutedDevice) 551 { 552 //Quick exit if execution should not occur 553 if (firstUnexecutedDevice == this) 554 return; 555 556 selectedOutput.executeUpTo(firstUnexecutedDevice); 557 } 558 559 public void executeFromStartOfChainUpTo(SignalProcessor firstUnexecutedDevice) 560 { 561 selectedInput.executeFromStartOfChainUpTo(firstUnexecutedDevice); 562 } 563 }