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    }