001package jmri.implementation; 002 003import java.beans.PropertyChangeListener; 004import java.util.ArrayList; 005import java.util.List; 006import jmri.InstanceManager; 007import jmri.NamedBean; 008import jmri.NamedBeanHandle; 009import jmri.NamedBeanHandleManager; 010import jmri.NamedBeanUsageReport; 011import jmri.Sensor; 012import jmri.SignalHead; 013import jmri.SignalMast; 014import jmri.Turnout; 015import org.slf4j.Logger; 016import org.slf4j.LoggerFactory; 017 018/** 019 * A Conditional type to provide Signal Groups (n Signal Heads w/Conditionals 020 * for a main Mast). 021 * 022 * @see jmri.SignalGroup SignalGroup 023 * @author Pete Cressman Copyright (C) 2009 024 * @author Egbert Broerse 2017 025 */ 026public class DefaultSignalGroup extends AbstractNamedBean implements jmri.SignalGroup { 027 028 /** 029 * Constructor for SignalGroup instance. 030 * 031 * @param systemName suggested system name 032 * @param userName provided user name 033 */ 034 public DefaultSignalGroup(String systemName, String userName) { 035 super(systemName, userName); 036 } 037 038 /** 039 * Constructor for SignalGroup instance. 040 * 041 * @param systemName suggested system name 042 */ 043 public DefaultSignalGroup(String systemName) { 044 super(systemName, null); 045 log.debug("default SignalGroup {} created", systemName); 046 } 047 048 @Override 049 public String getBeanType() { 050 return Bundle.getMessage("BeanNameSignalGroup"); 051 } 052 053 ArrayList<String> _signalMastAspects = new ArrayList<String>(); 054 055 private NamedBeanHandle<SignalMast> _signalMast; 056 057 private boolean headactive = false; 058 059 private boolean enabled = true; 060 061 @Override 062 public void setEnabled(boolean boo) { 063 enabled = boo; 064 } 065 066 @Override 067 public boolean getEnabled() { 068 return enabled; 069 } 070 071 @Override 072 public void setSignalMast(String pName) { 073 SignalMast mMast = InstanceManager.getDefault(jmri.SignalMastManager.class).getBySystemName(pName); 074 if (mMast == null) { 075 mMast = InstanceManager.getDefault(jmri.SignalMastManager.class).getByUserName(pName); 076 } 077 if (mMast == null) { 078 log.warn("did not find a Signal Mast named {}", pName); 079 return; 080 } 081 setSignalMast(mMast, pName); 082 } 083 084 @Override 085 public void setSignalMast(SignalMast signalMast, String mastName) { 086 if (_signalMast != null) { 087 getSignalMast().removePropertyChangeListener(mSignalMastListener); 088 } 089 _signalMast = InstanceManager.getDefault(NamedBeanHandleManager.class) 090 .getNamedBeanHandle(mastName, signalMast); 091 getSignalMast().addPropertyChangeListener(mSignalMastListener = new java.beans.PropertyChangeListener() { 092 @Override 093 public void propertyChange(java.beans.PropertyChangeEvent e) { 094 if (e.getPropertyName().equals("Aspect")) { 095 String now = ((String) e.getNewValue()); 096 if (isSignalMastAspectIncluded(now)) { 097 setHead(); 098 } else { 099 resetHeads(); 100 } 101 } 102 } 103 }); 104 } 105 106 @Override 107 public SignalMast getSignalMast() { 108 return _signalMast.getBean(); 109 } 110 111 @Override 112 public String getSignalMastName() { 113 return _signalMast.getName(); 114 } 115 116 @Override 117 public void addSignalMastAspect(String aspect) { 118 if (isSignalMastAspectIncluded(aspect)) { 119 return; 120 } 121 _signalMastAspects.add(aspect); 122 } 123 124 @Override 125 public boolean isSignalMastAspectIncluded(String aspect) { 126 for (int i = 0; i < _signalMastAspects.size(); i++) { 127 if (_signalMastAspects.get(i).equals(aspect)) { 128 // Found Aspect 129 return true; 130 } 131 } 132 return false; 133 } 134 135 @Override 136 public void deleteSignalMastAspect(String aspect) { 137 _signalMastAspects.remove(aspect); 138 } 139 140 @Override 141 public int getNumSignalMastAspects() { 142 return _signalMastAspects.size(); 143 } 144 145 @Override 146 public String getSignalMastAspectByIndex(int x) { 147 try { 148 return _signalMastAspects.get(x); 149 } catch (IndexOutOfBoundsException ioob) { 150 return null; 151 } 152 } 153 154 @Override 155 public void clearSignalMastAspect() { 156 _signalMastAspects = new ArrayList<String>(); 157 } 158 159 @Override 160 public void addSignalHead(NamedBeanHandle<SignalHead> headBean) { 161 SignalHeadItem shi = new SignalHeadItem(headBean); 162 _signalHeadItem.add(shi); 163 } 164 165 /** 166 * Add a new Signal Head to the group by name. 167 * 168 * @param pName system or username of existing signal head to add to group 169 */ 170 public void addSignalHead(String pName) { 171 SignalHead mHead = InstanceManager.getDefault(jmri.SignalHeadManager.class).getBySystemName(pName); 172 if (mHead == null) { 173 mHead = InstanceManager.getDefault(jmri.SignalHeadManager.class).getByUserName(pName); 174 } 175 if (mHead == null) { 176 log.warn("did not find a SignalHead named {}", pName); 177 } else { 178 addSignalHead(InstanceManager.getDefault(NamedBeanHandleManager.class) 179 .getNamedBeanHandle(pName, mHead)); 180 } 181 } 182 183 @Override 184 public void addSignalHead(SignalHead signalHead) { 185 addSignalHead(InstanceManager.getDefault(NamedBeanHandleManager.class) 186 .getNamedBeanHandle(signalHead.getDisplayName(), signalHead)); 187 } 188 189 protected PropertyChangeListener mSignalMastListener = null; 190 191 @Override 192 public void setHeadAlignTurnout(SignalHead signalHead, Turnout turnout, int state) { 193 SignalHeadItem shi = getHeadItem(signalHead); 194 shi.addTurnout(turnout, state); 195 } 196 197 @Override 198 public void setHeadAlignSensor(SignalHead signalHead, Sensor sensor, int state) { 199 SignalHeadItem shi = getHeadItem(signalHead); 200 shi.addSensor(sensor, state); 201 } 202 203 private SignalHeadItem getHeadItemByIndex(int x) { 204 try { 205 return _signalHeadItem.get(x); 206 } catch (IndexOutOfBoundsException ioob) { 207 return null; 208 } 209 } 210 211 @Override 212 public String getHeadItemNameByIndex(int x) { 213 try { 214 return getHeadItemByIndex(x).getName(); 215 } catch (IndexOutOfBoundsException ioob) { 216 return null; 217 } 218 } 219 220 @Override 221 public SignalHead getHeadItemBeanByIndex(int x) { 222 try { 223 return getHeadItemByIndex(x).getSignalHead(); 224 } catch (IndexOutOfBoundsException ioob) { 225 return null; 226 } 227 } 228 229 @Override 230 public int getNumHeadItems() { 231 return _signalHeadItem.size(); 232 } 233 234 @Override 235 public int getHeadOffState(SignalHead headBean) { 236 try { 237 return getHeadItem(headBean).getOffAppearance(); 238 } catch (NullPointerException e) { 239 return -1; 240 } 241 } 242 243 @Override 244 public int getHeadOnState(SignalHead headBean) { 245 try { 246 return getHeadItem(headBean).getOnAppearance(); 247 } catch (NullPointerException e) { 248 return -1; 249 } 250 } 251 252 @Override 253 public int getHeadOnStateByIndex(int x) { 254 try { 255 return getHeadItemByIndex(x).getOnAppearance(); 256 } catch (IndexOutOfBoundsException ioob) { 257 return -1; 258 } 259 } 260 261 @Override 262 public int getHeadOffStateByIndex(int x) { 263 try { 264 return getHeadItemByIndex(x).getOffAppearance(); 265 } catch (IndexOutOfBoundsException ioob) { 266 return -1; 267 } 268 } 269 270 @Override 271 public void deleteSignalHead(SignalHead sh) { 272 _signalHeadItem.remove(getHeadItem(sh)); 273 } 274 275 @Override 276 public void deleteSignalHead(NamedBeanHandle<SignalHead> headBean) { 277 _signalHeadItem.remove(getHeadItem(headBean.getName())); 278 } 279 280 @Override 281 public void setHeadOnState(SignalHead head, int state) { 282 getHeadItem(head).setOnAppearance(state); 283 firePropertyChange("UpdateCondition", null, null); 284 } 285 286 @Override 287 public void setHeadOffState(SignalHead head, int state) { 288 getHeadItem(head).setOffAppearance(state); 289 firePropertyChange("UpdateCondition", null, null); 290 } 291 292 @Override 293 public boolean isHeadIncluded(SignalHead signalHead) { 294 for (int i = 0; i < _signalHeadItem.size(); i++) { 295 if (_signalHeadItem.get(i).getSignalHead() == signalHead) { 296 // Found head 297 return true; 298 } 299 } 300 return false; 301 } 302 303 /** 304 * Get a Signal Head item by its name from the Signal Group 305 */ 306 private SignalHeadItem getHeadItem(String name) { 307 for (int i = 0; i < _signalHeadItem.size(); i++) { 308 if (_signalHeadItem.get(i).getName().equals(name)) { 309 return _signalHeadItem.get(i); 310 } 311 } 312 return null; 313 } 314 315 /** 316 * Get a Signal Head item by its Bean from the Signal Group 317 */ 318 private SignalHeadItem getHeadItem(NamedBean headBean) { 319 for (int i = 0; i < _signalHeadItem.size(); i++) { 320 if (_signalHeadItem.get(i).getSignalHead().equals(headBean)) { 321 return _signalHeadItem.get(i); 322 } 323 } 324 return null; 325 } 326 327 @Override 328 public boolean isTurnoutIncluded(SignalHead signalHead, Turnout turnout) { 329 return getHeadItem(signalHead).isTurnoutIncluded(turnout); 330 } 331 332 @Override 333 public int getTurnoutState(SignalHead signalHead, Turnout turnout) { 334 SignalHeadItem shi = getHeadItem(signalHead); 335 if (shi != null) { 336 return shi.getTurnoutState(turnout); 337 } 338 return -1; 339 } 340 341 @Override 342 public int getTurnoutStateByIndex(int x, Turnout turnout) { 343 try { 344 return getHeadItemByIndex(x).getTurnoutState(turnout); 345 } catch (IndexOutOfBoundsException ioob) { 346 return -1; 347 } 348 } 349 350 @Override 351 public int getTurnoutStateByIndex(int x, int pTurnout) { 352 try { 353 return getHeadItemByIndex(x).getTurnoutState(pTurnout); 354 } catch (IndexOutOfBoundsException ioob) { 355 return -1; 356 } 357 } 358 359 @Override 360 public String getTurnoutNameByIndex(int x, int pTurnout) { 361 try { 362 return getHeadItemByIndex(x).getTurnoutName(pTurnout); 363 } catch (IndexOutOfBoundsException ioob) { 364 return null; 365 } 366 } 367 368 @Override 369 public Turnout getTurnoutByIndex(int x, int pTurnout) { 370 try { 371 return getHeadItemByIndex(x).getTurnout(pTurnout); 372 } catch (IndexOutOfBoundsException ioob) { 373 return null; 374 } 375 } 376 377 @Override 378 public int getSensorStateByIndex(int x, int pSensor) { 379 try { 380 return getHeadItemByIndex(x).getSensorState(pSensor); 381 } catch (IndexOutOfBoundsException ioob) { 382 return -1; 383 } 384 } 385 386 @Override 387 public String getSensorNameByIndex(int x, int pSensor) { 388 try { 389 return getHeadItemByIndex(x).getSensorName(pSensor); 390 } catch (IndexOutOfBoundsException ioob) { 391 return null; 392 } 393 } 394 395 @Override 396 public Sensor getSensorByIndex(int x, int pSensor) { 397 try { 398 return getHeadItemByIndex(x).getSensor(pSensor); 399 } catch (IndexOutOfBoundsException ioob) { 400 return null; 401 } 402 } 403 404 @Override 405 public boolean isSensorIncluded(SignalHead signalHead, Sensor sensor) { 406 return getHeadItem(signalHead).isSensorIncluded(sensor); 407 } 408 409 @Override 410 public int getSensorState(SignalHead signalHead, Sensor sensor) { 411 SignalHeadItem shi = getHeadItem(signalHead); 412 if (shi != null) { 413 return shi.getSensorState(sensor); 414 } 415 return -1; 416 } 417 418 @Override 419 public boolean getSensorTurnoutOper(SignalHead signalHead) { 420 return getHeadItem(signalHead).getSensorTurnoutOper(); 421 } 422 423 @Override 424 public boolean getSensorTurnoutOperByIndex(int x) { 425 return getHeadItemByIndex(x).getSensorTurnoutOper(); 426 } 427 428 @Override 429 public void setSensorTurnoutOper(SignalHead signalHead, boolean boo) { 430 getHeadItem(signalHead).setSensorTurnoutOper(boo); 431 firePropertyChange("UpdateCondition", null, null); 432 } 433 434 @Override 435 public void clearHeadTurnout(SignalHead signalHead) { 436 getHeadItem(signalHead).clearSignalTurnouts(); 437 } 438 439 @Override 440 public void clearHeadSensor(SignalHead signalHead) { 441 getHeadItem(signalHead).clearSignalSensors(); 442 } 443 444 private void resetHeads() { 445 if (!headactive) { 446 return; 447 } 448 for (int i = 0; i < _signalHeadItem.size(); i++) { 449 _signalHeadItem.get(i).getSignalHead().setAppearance(_signalHeadItem.get(i).getOffAppearance()); 450 } 451 headactive = false; 452 } 453 454 private void setHead() { 455 boolean active = false; 456 for (int i = 0; i < _signalHeadItem.size(); i++) { 457 if (_signalHeadItem.get(i).checkActive()) { 458 if (active) { 459 log.warn("two signal heads in the group should not be active at once"); 460 } 461 active = true; 462 headactive = true; 463 } 464 } 465 } 466 467 @Override 468 public int getNumHeadSensorsByIndex(int x) { 469 try { 470 471 return getHeadItemByIndex(x).getNumSensors(); 472 } catch (IndexOutOfBoundsException ioob) { 473 return -1; 474 } 475 } 476 477 @Override 478 public int getNumHeadTurnoutsByIndex(int x) { 479 try { 480 return getHeadItemByIndex(x).getNumTurnouts(); 481 } catch (IndexOutOfBoundsException ioob) { 482 return -1; 483 } 484 } 485 ArrayList<SignalHeadItem> _signalHeadItem = new ArrayList<SignalHeadItem>(); 486 487 private static class SignalHeadItem { 488 489 SignalHeadItem(NamedBeanHandle<SignalHead> sh) { 490 namedHead = sh; 491 if (namedHead.getBean().getClass().getName().contains("SingleTurnoutSignalHead")) { 492 jmri.implementation.SingleTurnoutSignalHead stsh = (jmri.implementation.SingleTurnoutSignalHead) namedHead.getBean(); 493 if ((onAppearance == 0x00) && (offAppearance == 0x00)) { 494 onAppearance = stsh.getOnAppearance(); 495 offAppearance = stsh.getOffAppearance(); 496 } 497 } 498 } 499 500 private NamedBeanHandle<SignalHead> namedHead; 501 502 public String getName() { 503 return namedHead.getName(); 504 } 505 506 public SignalHead getSignalHead() { 507 return namedHead.getBean(); 508 } 509 510 private int onAppearance = 0x00; 511 private int offAppearance = 0x00; 512 513 public void setOnAppearance(int app) { 514 onAppearance = app; 515 } 516 517 public int getOnAppearance() { 518 return onAppearance; 519 } 520 521 public void setOffAppearance(int app) { 522 offAppearance = app; 523 } 524 525 public int getOffAppearance() { 526 return offAppearance; 527 } 528 //Used to determine if we are using an AND or OR when testing the Sensors and Signals 529 private boolean turnoutSensorOper = true; 530 531 public boolean getSensorTurnoutOper() { 532 return turnoutSensorOper; 533 } 534 535 /** 536 * Set whether the sensors and turnouts should be treated as separate 537 * calculations (OR) or as one (AND), when determining if the Signal 538 * Head in this item should be On or Off. 539 * 540 * @param boo Provide true for AND, false for OR 541 */ 542 public void setSensorTurnoutOper(boolean boo) { 543 turnoutSensorOper = boo; 544 } 545 546 // Don't yet have the AND or OR set. 547 public boolean checkActive() { 548 boolean state = false; 549 for (int x = 0; x < _signalTurnoutList.size(); x++) { 550 log.debug("Real state {} {} state we testing for {}", _signalTurnoutList.get(x).getName(), _signalTurnoutList.get(x).getTurnout().getKnownState(), _signalTurnoutList.get(x).getState()); 551 if (_signalTurnoutList.get(x).getTurnout().getKnownState() == _signalTurnoutList.get(x).getState()) { 552 state = true; 553 } else { 554 state = false; 555 break; 556 } 557 } 558 for (int x = 0; x < _signalSensorList.size(); x++) { 559 if (_signalSensorList.get(x).getSensor().getKnownState() == _signalSensorList.get(x).getState()) { 560 state = true; 561 } else { 562 state = false; 563 break; 564 } 565 } 566 if (state) { 567 getSignalHead().setAppearance(onAppearance); 568 } else { 569 getSignalHead().setAppearance(offAppearance); 570 } 571 return state; 572 } 573 574 ArrayList<SignalTurnout> _signalTurnoutList = new ArrayList<SignalTurnout>(); 575 576 private static class SignalTurnout { 577 578 NamedBeanHandle<Turnout> _turnout; 579 int _state; 580 581 SignalTurnout(Turnout turn, int state) { 582 _turnout = jmri.InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(turn.getDisplayName(), turn); 583 setState(state); 584 } 585 586 String getName() { 587 if (_turnout != null) { 588 return _turnout.getName(); 589 } 590 return null; 591 } 592 593 boolean setState(int state) { 594 if (_turnout == null) { 595 return false; 596 } 597 if ((state != Turnout.THROWN) && (state != Turnout.CLOSED)) { 598 log.warn("Illegal Turnout state {} for : {}", state, getName()); 599 return false; 600 } 601 _state = state; 602 return true; 603 } 604 605 int getState() { 606 return _state; 607 } 608 609 Turnout getTurnout() { 610 return _turnout.getBean(); 611 } 612 } 613 614 void addTurnout(Turnout turn, int state) { 615 SignalTurnout signalTurnout = new SignalTurnout(turn, state); 616 _signalTurnoutList.add(signalTurnout); 617 } 618 619 Turnout getTurnout(int x) { 620 return _signalTurnoutList.get(x).getTurnout(); 621 } 622 623 int getTurnoutState(Turnout turn) { 624 for (int i = 0; i < _signalTurnoutList.size(); i++) { 625 if (_signalTurnoutList.get(i).getTurnout() == turn) { 626 return _signalTurnoutList.get(i).getState(); 627 } 628 } 629 return -1; 630 } 631 632 int getNumTurnouts() { 633 return _signalTurnoutList.size(); 634 } 635 636 String getTurnoutName(int x) { 637 return _signalTurnoutList.get(x).getName(); 638 } 639 640 int getTurnoutState(int x) { 641 return _signalTurnoutList.get(x).getState(); 642 } 643 644 boolean isTurnoutIncluded(Turnout turnout) { 645 for (int i = 0; i < _signalTurnoutList.size(); i++) { 646 if (_signalTurnoutList.get(i).getTurnout() == turnout) { 647 return true; 648 } 649 } 650 return false; 651 } 652 653 void clearSignalTurnouts() { 654 _signalTurnoutList = new ArrayList<SignalTurnout>(); 655 } 656 657 void clearSignalSensors() { 658 _signalSensorList = new ArrayList<SignalSensor>(); 659 } 660 661 ArrayList<SignalSensor> _signalSensorList = new ArrayList<SignalSensor>(); 662 663 private static class SignalSensor { 664 665 NamedBeanHandle<Sensor> _Sensor; 666 int _state; 667 668 SignalSensor(Sensor sen, int state) { 669 _Sensor = jmri.InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(sen.getDisplayName(), sen); 670 setState(state); 671 } 672 673 String getName() { 674 if (_Sensor != null) { 675 return _Sensor.getName(); 676 } 677 return null; 678 } 679 680 boolean setState(int state) { 681 if (_Sensor == null) { 682 return false; 683 } 684 if ((state != Sensor.ACTIVE) && (state != Sensor.INACTIVE)) { 685 log.warn("Illegal Sensor state {} for : {}", state, getName()); 686 return false; 687 } 688 _state = state; 689 return true; 690 } 691 692 int getState() { 693 return _state; 694 } 695 696 Sensor getSensor() { 697 return _Sensor.getBean(); 698 } 699 700 } 701 702 void addSensor(Sensor sen, int state) { 703 SignalSensor signalSensor = new SignalSensor(sen, state); 704 _signalSensorList.add(signalSensor); 705 } 706 707 int getSensorState(Sensor sen) { 708 for (int i = 0; i < _signalSensorList.size(); i++) { 709 if (_signalSensorList.get(i).getSensor() == sen) { 710 return _signalSensorList.get(i).getState(); 711 } 712 } 713 return -1; 714 } 715 716 int getNumSensors() { 717 return _signalSensorList.size(); 718 } 719 720 /*SignalSensor getSignalSensorByIndex(int x){ 721 return _signalSensorList.get(x); 722 }*/ 723 String getSensorName(int x) { 724 return _signalSensorList.get(x).getName(); 725 } 726 727 Sensor getSensor(int x) { 728 return _signalSensorList.get(x).getSensor(); 729 } 730 731 int getSensorState(int x) { 732 return _signalSensorList.get(x).getState(); 733 } 734 735 boolean isSensorIncluded(Sensor sensor) { 736 for (int i = 0; i < _signalSensorList.size(); i++) { 737 if (_signalSensorList.get(i).getSensor() == sensor) { 738 // Found Sensor 739 return true; 740 } 741 } 742 return false; 743 } 744 } 745 746 @Override 747 public int getState() { 748 return 0x00; 749 } 750 751 @Override 752 public void setState(int state) { 753 } 754 755 @Override 756 public List<NamedBeanUsageReport> getUsageReport(NamedBean bean) { 757 List<NamedBeanUsageReport> report = new ArrayList<>(); 758 if (bean != null) { 759 if (bean.equals(getSignalMast())) { 760 report.add(new NamedBeanUsageReport("SignalGroupMast")); // NOI18N 761 } 762 for (int i = 0; i < getNumHeadItems(); i++) { 763 if (bean.equals(getHeadItemBeanByIndex(i))) { 764 report.add(new NamedBeanUsageReport("SignalGroupHead")); // NOI18N 765 } 766 for (int j = 0; j < getNumHeadSensorsByIndex(i); j++) { 767 if (bean.equals(getSensorByIndex(i, j))) { 768 report.add(new NamedBeanUsageReport("SignalGroupHeadSensor")); // NOI18N 769 } 770 } 771 for (int k = 0; k < getNumHeadTurnoutsByIndex(i); k++) { 772 if (bean.equals(getTurnoutByIndex(i, k))) { 773 report.add(new NamedBeanUsageReport("SignalGroupHeadTurnout")); // NOI18N 774 } 775 } 776 } 777 } 778 return report; 779 } 780 781 private final static Logger log = LoggerFactory.getLogger(DefaultSignalGroup.class); 782 783}