001 package edu.nrao.sss.electronics; 002 003 /** 004 * A connector for moving a signal from one device to another. 005 * <p> 006 * A pipe has two ends, one for input and one for output. 007 * The input of a pipe may be either unconnected or connected to a 008 * {@link SignalSource signal source}. 009 * The output may likewise be either unconnected or connected to a 010 * {@link SignalProcessor signal processor}.</p> 011 * <p> 012 * Executing a pipe is the same as executing its output processor, 013 * provided it has one. Executing a pipe with a disconnected 014 * output does nothing. Executing a pipe with a disconnected input 015 * does whatever the processor connected to its output does when it 016 * has no signal.</p> 017 * <p> 018 * The connections of a pipe are normally forged in such a way 019 * that they may be unconnected and then reconnected to the same 020 * devices, or to different devices. Via the static factory methods, 021 * however, a pipe may be constructed with one of its ends <i>welded</i> 022 * to a device. A welded connection is permanent and may not be 023 * disconnected or connected to another device. Devices tend to 024 * create their pipes so that they are welded to themselves. Clients 025 * of these devices then connect their outputs to the inputs of 026 * other devices.</p> 027 * <p> 028 * <b>Version Info:</b> 029 * <table style="margin-left:2em"> 030 * <tr><td>$Revision: 1269 $</td></tr> 031 * <tr><td>$Date: 2008-05-05 14:39:39 -0600 (Mon, 05 May 2008) $</td></tr> 032 * <tr><td>$Author: dharland $ (last person to modify)</td></tr> 033 * </table></p> 034 * 035 * @author David M. Harland 036 * @since 2007-10-25 037 */ 038 public class SignalPipe 039 implements SignalSource, SignalProcessor 040 { 041 private static final String DEFAULT_NAME = "[unnamed pipe]"; 042 043 private String name; 044 045 SignalSource input; 046 SignalProcessor output; 047 048 private boolean inputIsWelded; 049 private boolean outputIsWelded; 050 051 //============================================================================ 052 // OBJECT CREATION 053 //============================================================================ 054 055 /** 056 * Creates a new unnamed pipe that is not connected on either of its ends. 057 */ 058 public SignalPipe() 059 { 060 this(null); 061 } 062 063 /** 064 * Creates a new named pipe that is not connected on either of its ends. 065 * 066 * @param pipeName 067 * an optional name for this pipe. If this value is <i>null</i> a 068 * default name will be used in its place. 069 */ 070 public SignalPipe(String pipeName) 071 { 072 name = (pipeName == null) ? DEFAULT_NAME : pipeName; 073 074 input = null; 075 output = null; 076 077 inputIsWelded = false; 078 outputIsWelded = false; 079 } 080 081 /** 082 * Creates a new pipe, welds its input to {@code source}, and returns it. 083 * If {@code source} is a pipe whose output is welded to another processor, 084 * the returned pipe will be <i>null</i>. 085 * 086 * @param source 087 * a source to be welded to the input of the returned pipe. 088 * 089 * @return a new pipe whose input is welded to {@code source}, 090 * or <i>null</i> if the weld could not be made. 091 */ 092 public static SignalPipe makeAndWeldInputTo(SignalSource source) 093 { 094 SignalPipe newPipe = new SignalPipe(); 095 096 boolean success = newPipe.connectInputTo(source); 097 098 if (success) 099 { 100 newPipe.inputIsWelded = true; 101 102 if (source instanceof SignalPipe) 103 ((SignalPipe)source).outputIsWelded = true; 104 } 105 else 106 { 107 newPipe = null; 108 } 109 110 return newPipe; 111 } 112 113 /** 114 * Creates a new pipe, welds its output to {@code processor}, and returns it. 115 * If {@code processor} is a pipe whose input is welded to another source, 116 * the returned pipe will be <i>null</i>. 117 * 118 * @param processor 119 * a processor to be welded to the output of the returned pipe. 120 * 121 * @return a new pipe whose output is welded to {@code processor}, 122 * or <i>null</i> if the weld could not be made. 123 */ 124 public static SignalPipe makeAndWeldOutputTo(SignalProcessor processor) 125 { 126 SignalPipe newPipe = new SignalPipe(); 127 128 boolean success = newPipe.connectOutputTo(processor); 129 130 if (success) 131 { 132 newPipe.outputIsWelded = true; 133 134 if (processor instanceof SignalPipe) 135 ((SignalPipe)processor).inputIsWelded = true; 136 } 137 else 138 { 139 newPipe = null; 140 } 141 142 return newPipe; 143 } 144 145 //============================================================================ 146 // NAMING 147 //============================================================================ 148 149 /** 150 * Sets a new name for this pipe. 151 * Clients are not required to name their pipes. 152 * <p> 153 * If {@code newName} is <i>null</i> or the empty string 154 * (<tt>""</tt>), the request to change the name will be 155 * denied and the current name will remain in place.</p> 156 * 157 * @param newName 158 * the new name of this pipe. 159 */ 160 public void setName(String newName) 161 { 162 if (newName != name && newName.length() > 0) 163 name = newName; 164 } 165 166 /** 167 * Returns the name of this pipe. 168 * The returned value will be either a client-specified name or a default 169 * name. It will never be <i>null</i>. 170 * 171 * @return the name of this pipe. 172 */ 173 public String getName() 174 { 175 return name; 176 } 177 178 //============================================================================ 179 // CONNECTING AND DISCONNECTING 180 //============================================================================ 181 182 /** 183 * Returns <i>true</i> if the input of this pipe may be connected 184 * to {@code source}. 185 * <p> 186 * This method will return <i>true</i> if, and only if,</p> 187 * <ol> 188 * <li>the input of this pipe is not welded to another source, and</li> 189 * <li>{@code source} is not a pipe whose output is welded to another 190 * processor.</li> 191 * </ol> 192 * 193 * @param source 194 * the device to be tested for connectability to input of this pipe. 195 * 196 * @return <i>true</i> if the input of this pipe may be connected 197 * to {@code source}. 198 */ 199 public boolean canConnectInputTo(SignalSource source) 200 { 201 //This pipe can connect so long as its input has not been welded 202 boolean canConnect = !inputIsWelded; 203 204 //If the source is a pipe, and if that pipe's output 205 //has been welded, then we cannot connect. 206 if (canConnect && (source instanceof SignalPipe)) 207 canConnect = !((SignalPipe)source).outputIsWelded; 208 209 return canConnect; 210 } 211 212 /** 213 * Attempts to connect the input of this pipe to {@code source} and returns 214 * <i>true</i> if successful. If the attempt was unsuccessful, this pipe 215 * will be in the same state it was in just prior to the call to this method. 216 * 217 * @param source 218 * the source to which the input of this pipe should be connected. 219 * 220 * @return <i>true</i> if the attempt to connect was successful. 221 */ 222 public boolean connectInputTo(SignalSource source) 223 { 224 boolean success = canConnectInputTo(source); 225 226 if (success) 227 { 228 //This sets our input to null, and if our old source was a pipe, 229 //it also sets its output to null. 230 success = disconnectInput(); 231 232 if (success) 233 { 234 this.input = source; 235 236 //The call to canConnectInputTo told us that the source pipe, 237 //if the source is a pipe, has an unwelded output. 238 if (source instanceof SignalPipe) 239 ((SignalPipe)source).output = this; 240 } 241 } 242 243 return success; 244 } 245 246 /** 247 * Attempts to disconnect the input of this pipe and returns 248 * <i>true</i> if successful. The attempt will be unsuccessful 249 * if the input of this pipe is welded to its device. 250 * If the attempt was unsuccessful, this pipe will be in the 251 * same state it was in just prior to the call to this method. 252 * 253 * @return <i>true</i> if the attempt to disconnect was successful. 254 */ 255 public boolean disconnectInput() 256 { 257 //This pipe is OK so long as its input has not been welded 258 boolean canDisconnect = !inputIsWelded; 259 260 if (canDisconnect) 261 { 262 //If our input is not welded, and if our source is a pipe, 263 //tell that pipe that its output is being disconnected. 264 if (input instanceof SignalPipe) 265 { 266 SignalPipe inputPipe = (SignalPipe)input; 267 268 assert inputPipe.output == this : "Output of my input was not me."; 269 270 assert !inputPipe.outputIsWelded : "Inconsistent welds."; 271 272 inputPipe.output = null; 273 } 274 275 //This was really all we were trying to do: 276 this.input = null; 277 } 278 279 return canDisconnect; 280 } 281 282 /** 283 * Returns <i>true</i> if the output of this pipe may be connected 284 * to {@code processor}. 285 * <p> 286 * This method will return <i>true</i> if, and only if,</p> 287 * <ol> 288 * <li>the output of this pipe is not welded to another processor, and</li> 289 * <li>{@code processor} is not a pipe whose input is welded to another 290 * source.</li> 291 * </ol> 292 * 293 * @param processor 294 * the device to be tested for connectability to the output of this pipe. 295 * 296 * @return <i>true</i> if the input of this pipe may be connected 297 * to {@code source}. 298 */ 299 public boolean canConnectOutputTo(SignalProcessor processor) 300 { 301 //This pipe can connect so long as its output has not been welded 302 boolean canConnect = !outputIsWelded; 303 304 //If the processor is a pipe, and if that pipe's input 305 //has been welded, then we cannot connect. 306 if (canConnect && (processor instanceof SignalPipe)) 307 canConnect = !((SignalPipe)processor).inputIsWelded; 308 309 return canConnect; 310 } 311 312 /** 313 * Attempts to connect the output of this pipe to {@code processor} 314 * and returns <i>true</i> if successful. 315 * If the attempt was unsuccessful, this pipe will be in the same 316 * state it was in just prior to the call to this method. 317 * 318 * @param processor 319 * the processor to which the output of this pipe should be connected. 320 * 321 * @return <i>true</i> if the attempt to connect was successful. 322 */ 323 public boolean connectOutputTo(SignalProcessor processor) 324 { 325 boolean success = canConnectOutputTo(processor); 326 327 if (success) 328 { 329 //This sets our output to null, and if our old processor was a pipe, 330 //it also sets its input to null. 331 success = disconnectOutput(); 332 333 if (success) 334 { 335 this.output = processor; 336 337 //The call to canConnectInputTo told us that the source pipe, 338 //if the source is a pipe, has an unwelded output. 339 if (processor instanceof SignalPipe) 340 ((SignalPipe)processor).input = this; 341 } 342 } 343 344 return success; 345 } 346 347 /** 348 * Attempts to disconnect the output of this pipe and returns 349 * <i>true</i> if successful. The attempt will be unsuccessful 350 * if the output of this pipe is welded to its device. 351 * If the attempt was unsuccessful, this pipe will be in the 352 * same state it was in just prior to the call to this method. 353 * 354 * @return <i>true</i> if the attempt to disconnect was successful. 355 */ 356 public boolean disconnectOutput() 357 { 358 //This pipe is OK so long as its output has not been welded 359 boolean canDisconnect = !outputIsWelded; 360 361 if (canDisconnect) 362 { 363 //If our output is not welded, and if our processor is a pipe, 364 //tell that pipe that its input is being disconnected. 365 if (output instanceof SignalPipe) 366 { 367 SignalPipe outputPipe = (SignalPipe)output; 368 369 assert outputPipe.input == this : "Input of my output was not me."; 370 371 assert !outputPipe.inputIsWelded : "Inconsistent welds."; 372 373 outputPipe.input = null; 374 } 375 376 //This was really all we were trying to do: 377 this.output = null; 378 } 379 380 return canDisconnect; 381 } 382 383 //============================================================================ 384 // INTERFACE SignalSource 385 //============================================================================ 386 387 /** 388 * Returns the signal of the source connected to the input of this pipe. 389 * If nothing is connected to this pipe, or if the connected 390 * source has a <i>null</i> signal, <i>null</i> will be returned. 391 */ 392 public Signal getSignal() 393 { 394 if (input instanceof SignalSwitch) 395 { 396 SignalSwitch ss = (SignalSwitch)input; 397 return ss.getSignal(this); 398 } 399 else 400 { 401 return input == null ? null : input.getSignal(); //TODO should this clone the signal? 402 } 403 } 404 405 //============================================================================ 406 // INTERFACE SignalProcessor 407 //============================================================================ 408 409 /** 410 * Executes the processor connected to the output of this pipe, if any. 411 */ 412 public void execute() 413 { 414 if (outputIsConnected()) 415 output.execute(); 416 } 417 418 public void executeUpTo(SignalProcessor firstUnexecutedDevice) 419 { 420 if (outputIsConnected() && firstUnexecutedDevice != this) 421 output.executeUpTo(firstUnexecutedDevice); 422 } 423 424 public void executeFromStartOfChainUpTo(SignalProcessor firstUnexecutedDevice) 425 { 426 if (inputIsConnected() && input instanceof SignalProcessor) 427 { 428 ((SignalProcessor)input).executeFromStartOfChainUpTo(firstUnexecutedDevice); 429 } 430 else 431 { 432 executeUpTo(firstUnexecutedDevice); 433 } 434 } 435 436 private boolean outputIsConnected() 437 { 438 //If output is not null, momentarily consider it to be connected 439 boolean connected = (output != null); 440 441 //If output is to a sig switch, make sure we are its selected input 442 if (connected && output instanceof SignalSwitch) 443 connected = (((SignalSwitch)output).getSelectedInput() == this); 444 445 return connected; 446 } 447 448 private boolean inputIsConnected() 449 { 450 //If input is not null, momentarily consider it to be connected 451 boolean connected = (input != null); 452 453 //If input is from a sig switch, make sure we are its selected output 454 if (connected && input instanceof SignalSwitch) 455 connected = (((SignalSwitch)input).getSelectedOutput() == this); 456 457 return connected; 458 } 459 }