001/* 002 * @author Gregory J. Bedlek Copyright (C) 2018, 2019 003 */ 004package jmri.jmrit.ctc; 005 006import java.awt.event.ActionListener; 007import java.beans.PropertyChangeEvent; 008import java.beans.PropertyChangeListener; 009import javax.swing.Timer; 010import jmri.Sensor; 011import jmri.Turnout; 012 013/* 014If you have REAL feedback from the switch that sets it's position: 015 You SHOULD set "Feedback" to "True" and rely on this message to set the switch in "correspondence". 016 If you didn't set "Feedback" to "True" in this case, and the timeout amount was shorter than 017 the time it took the switch to "feed back" it's information, the switch will be OUT OF CORRESPONDENCE 018 and both lights will REMAIN unlit. In this case, throwing it AGAIN will eventually report proper! 019 If the "feedback" message from the unit is lost, then the lights will REMAIN (properly!) out of correspondence! 020 021This object ACTUALLY CONTROLS THE SWITCH, AND sends commands to move the points. 022*/ 023 024public class SwitchDirectionIndicators { 025 private NBHSensor _mNormalIndicatorSensor; 026 private NBHSensor _mReversedIndicatorSensor; 027 private NBHTurnout _mActualTurnout; 028 private PropertyChangeListener _mActualTurnoutPropertyChangeListener = null; 029 private boolean _mActualTurnoutHasFeedback; 030 private boolean _mWaitingForFeedbackOrTimer = false; 031 private int _mLastActualTurnoutState = CTCConstants.CTC_UNKNOWN; 032 private Timer _mSimulatedTurnoutFeedbackTimer = null; 033 private ActionListener _mSimulatedTurnoutFeedbackTimerActionListener = null; 034 private int _mLastIndicatorState = CTCConstants.OUTOFCORRESPONDENCE; 035 036 public SwitchDirectionIndicators( String userIdentifier, 037 NBHSensor normalIndicatorSensor, 038 NBHSensor reveresedIndicatorSensor, 039 NBHTurnout actualTurnout, 040 int codingTimeInMilliseconds, // Instead of "CodingDistrict" 041 boolean feedbackDifferent) { 042 _mNormalIndicatorSensor = normalIndicatorSensor; 043 _mReversedIndicatorSensor = reveresedIndicatorSensor; 044 _mActualTurnout = actualTurnout; 045 _mActualTurnoutHasFeedback = _mActualTurnout.getFeedbackMode() != Turnout.DIRECT && _mActualTurnout.getFeedbackMode() != Turnout.MONITORING; 046 047 if (_mActualTurnoutHasFeedback) { 048 // Let real sensor that drives turnout feedback set indicators: 049 _mActualTurnoutPropertyChangeListener = (PropertyChangeEvent e) -> { if (e.getPropertyName().equals("KnownState")) setSwitchIndicationSensorsToPresentState(); }; // NOI18N 050 _mActualTurnout.addPropertyChangeListener(_mActualTurnoutPropertyChangeListener); 051 setSwitchIndicationSensorsToPresentState(); 052 } else { // Simulate feedback delay if any: 053 _mSimulatedTurnoutFeedbackTimerActionListener = (ActionEvent) -> { setSwitchIndicationSensorsToPresentState(); }; 054 _mSimulatedTurnoutFeedbackTimer = new Timer(codingTimeInMilliseconds, _mSimulatedTurnoutFeedbackTimerActionListener); 055 _mSimulatedTurnoutFeedbackTimer.setRepeats(false); 056/* IF AND ONLY IF the layout owner DOES NOT initialize all DIRECT or MONITORING turnouts BEFORE 057 starting this CTC system, we wind up with turnouts that have KnownState = UNKNOWN 058 Here, IF AND ONLY IF the layout owner initialized those turnouts prior to starting this CTC system, 059 we now "give a chance" for us to find out the real state, and update the state if know, otherwise, 060 we ASSUME it is SWITCHNORMAL for those people who don't. 061*/ 062 setSwitchIndicationSensorsToPresentState(); 063 if (_mLastActualTurnoutState == CTCConstants.CTC_UNKNOWN) { 064 setSwitchIndicatorSensors(CTCConstants.SWITCHNORMAL); 065 } 066 } 067 } 068 069 public void removeAllListeners() { 070 _mActualTurnout.removePropertyChangeListener(_mActualTurnoutPropertyChangeListener); 071 if (_mSimulatedTurnoutFeedbackTimer != null) { 072 _mSimulatedTurnoutFeedbackTimer.stop(); 073 _mSimulatedTurnoutFeedbackTimer.removeActionListener(_mSimulatedTurnoutFeedbackTimerActionListener); 074 } 075 } 076 077 public void codeButtonPressed(int requestedState) { 078 if (_mWaitingForFeedbackOrTimer) return; // We've already done something in the past, ignore new request 079 if (requestedState != _mLastActualTurnoutState) { // It's different: 080 _mWaitingForFeedbackOrTimer = true; // We're doing something now! 081 setSwitchIndicatorSensors(CTCConstants.OUTOFCORRESPONDENCE); 082 if (requestedState == CTCConstants.SWITCHNORMAL) { _mActualTurnout.setCommandedState(Turnout.CLOSED); } 083 else if (requestedState == CTCConstants.SWITCHREVERSED) { _mActualTurnout.setCommandedState(Turnout.THROWN); } // Any other passed invalid value is ignored. 084 if (!_mActualTurnoutHasFeedback) _mSimulatedTurnoutFeedbackTimer.start(); // Simulate feedback (fire timer only once) 085 } 086 } 087 088 private int getPresentState() { 089 int actualTurnoutKnownState = _mActualTurnout.getKnownState(); 090 if (actualTurnoutKnownState == Turnout.CLOSED) _mLastActualTurnoutState = CTCConstants.SWITCHNORMAL; 091 else if (actualTurnoutKnownState == Turnout.THROWN) _mLastActualTurnoutState = CTCConstants.SWITCHREVERSED; 092 else _mLastActualTurnoutState = CTCConstants.CTC_UNKNOWN; 093 return _mLastActualTurnoutState; 094 } 095 096 public int getLastIndicatorState() { 097 return _mLastIndicatorState; 098 } 099 100 public boolean inCorrespondence() { 101 if (_mLastActualTurnoutState == CTCConstants.CTC_UNKNOWN) return true; // Fake out calling routine to allow it to call us at "CodeButtonPressed" 102 return _mLastIndicatorState != CTCConstants.OUTOFCORRESPONDENCE; 103 } 104 105 public NBHSensor getProperIndicatorSensor(boolean isNormal) { 106 return isNormal ? _mNormalIndicatorSensor : _mReversedIndicatorSensor; 107 } 108 109 private void setSwitchIndicatorSensors(int requestedState) { 110 switch(requestedState) { 111 case CTCConstants.SWITCHNORMAL: 112 _mNormalIndicatorSensor.setKnownState(Sensor.ACTIVE); 113 _mReversedIndicatorSensor.setKnownState(Sensor.INACTIVE); 114 _mLastIndicatorState = CTCConstants.SWITCHNORMAL; 115 break; 116 case CTCConstants.SWITCHREVERSED: 117 _mNormalIndicatorSensor.setKnownState(Sensor.INACTIVE); 118 _mReversedIndicatorSensor.setKnownState(Sensor.ACTIVE); 119 _mLastIndicatorState = CTCConstants.SWITCHREVERSED; 120 break; 121 default: 122 _mNormalIndicatorSensor.setKnownState(Sensor.INACTIVE); 123 _mReversedIndicatorSensor.setKnownState(Sensor.INACTIVE); 124 _mLastIndicatorState = CTCConstants.OUTOFCORRESPONDENCE; 125 break; 126 } 127 } 128 129 private void setSwitchIndicationSensorsToPresentState() { 130 if (_mSimulatedTurnoutFeedbackTimer != null) _mSimulatedTurnoutFeedbackTimer.stop(); 131 _mWaitingForFeedbackOrTimer = false; 132 setSwitchIndicatorSensors(getPresentState()); 133 } 134 135// private int getTransmissionDelayInMilliseconds(CodingDistrict codingDistrict) { 136// if (codingDistrict != null) return codingDistrict.getTransmissionDelayInMilliseconds(); 137// return 0; 138// } 139}