001package jmri.jmrit.entryexit;
002
003import java.beans.PropertyChangeEvent;
004import java.beans.PropertyChangeListener;
005import java.util.Hashtable;
006import java.util.List;
007import java.util.Map.Entry;
008import jmri.InstanceManager;
009import jmri.NamedBean;
010import jmri.Sensor;
011import jmri.SignalHead;
012import jmri.SignalMast;
013import jmri.jmrit.display.EditorManager;
014import jmri.jmrit.display.SensorIcon;
015import jmri.jmrit.display.layoutEditor.LayoutBlock;
016import jmri.jmrit.display.layoutEditor.LayoutEditor;
017import jmri.jmrit.display.layoutEditor.LayoutSlip;
018import jmri.jmrit.display.layoutEditor.LayoutTurnout;
019import jmri.jmrit.display.layoutEditor.LevelXing;
020import jmri.jmrit.display.layoutEditor.PositionablePoint;
021import org.slf4j.Logger;
022import org.slf4j.LoggerFactory;
023
024public class PointDetails {
025
026    //May want to look at putting a listener on the refLoc to listen to updates to blocks, signals and sensors attached to it
027    LayoutEditor panel = null;
028    LayoutBlock facing;
029    List<LayoutBlock> protectingBlocks;
030    private NamedBean refObj;
031    private Object refLoc;
032    private Sensor sensor;
033    private SignalMast signalmast;
034    private SignalHead signalhead;
035
036    static int nxButtonTimeout = 10;
037
038    Source sourceRoute;
039    transient Hashtable<DestinationPoints, Source> destinations = new Hashtable<>(5);
040
041    public PointDetails(LayoutBlock facing, List<LayoutBlock> protecting) {
042        this.facing = facing;
043        this.protectingBlocks = protecting;
044    }
045
046    public LayoutBlock getFacing() {
047        return facing;
048    }
049
050    public List<LayoutBlock> getProtecting() {
051        return protectingBlocks;
052    }
053
054    //This might be better off a ref to the source pointdetail.
055    boolean routeToSet = false;
056
057    void setRouteTo(boolean boo) {
058        routeToSet = boo;
059    }
060
061    boolean routeFromSet = false;
062
063    void setRouteFrom(boolean boo) {
064        routeFromSet = boo;
065    }
066
067    public void setPanel(LayoutEditor panel) {
068        this.panel = panel;
069        // find the panel that actually contains this sensor, default to the supplied panel
070        for (LayoutEditor layout : InstanceManager.getDefault(EditorManager.class).getAll(LayoutEditor.class)) {
071            for (SensorIcon si : layout.getSensorList()) {
072                if (sensor == si.getNamedBean()) {
073                    this.panel = layout;
074                    return;
075                }
076            }
077        }
078    }
079
080    void setSensor(Sensor sen) {
081        if (sensor == sen) {
082            return;
083        }
084        if (sensor != null) {
085            sensor.removePropertyChangeListener(nxButtonListener);
086        }
087        sensor = sen;
088        if (sensor != null) {
089            sensor.addPropertyChangeListener(nxButtonListener);
090        }
091    }
092
093    void addSensorList() {
094        sensor.addPropertyChangeListener(nxButtonListener);
095    }
096
097    void removeSensorList() {
098        sensor.removePropertyChangeListener(nxButtonListener);
099    }
100
101    //Sensor getSensor() { return sensor; }
102    protected PropertyChangeListener nxButtonListener = new PropertyChangeListener() {
103        //First off if we were inactive, and now active
104        @Override
105        public void propertyChange(PropertyChangeEvent e) {
106            nxButtonStateChange(e);
107        }
108    };
109
110    private void nxButtonStateChange(PropertyChangeEvent e) {
111        if (!e.getPropertyName().equals("KnownState")) {  // NOI18N
112            return;
113        }
114        int now = ((Integer) e.getNewValue());
115        int old = ((Integer) e.getOldValue());
116
117        if ((old == Sensor.UNKNOWN) || (old == Sensor.INCONSISTENT)) {
118            setButtonState(EntryExitPairs.NXBUTTONINACTIVE);
119            return;
120        }
121
122        DestinationPoints destPoint = null;
123
124        for (Entry<DestinationPoints, Source> dp : destinations.entrySet()) {
125            // This point might be a destination in zero or more NX pairs.
126            // If the source point is active, then this is a single point route.
127            destPoint = dp.getKey();
128            if (destPoint.isEnabled() && dp.getValue().getPoint().getNXState() == EntryExitPairs.NXBUTTONSELECTED) {
129                // This destination point has an active source point
130                setButtonState(EntryExitPairs.NXBUTTONSELECTED);
131                // Call activeBean directly
132                destPoint.activeBean(false);
133                log.debug("[nxButtonStateChange] Single point route selected");
134                return;
135            }
136        }
137
138        if (sourceRoute != null) {
139            if (now == Sensor.ACTIVE && getNXState() == EntryExitPairs.NXBUTTONINACTIVE) {
140                setButtonState(EntryExitPairs.NXBUTTONSELECTED);
141                for (Entry<PointDetails, DestinationPoints> en : sourceRoute.pointToDest.entrySet()) {
142                    //Sensor sen = getSensorFromPoint(en.getKey().getPoint());
143                    //Set a time out on the source sensor, so that if its state hasn't been changed, then we will clear it out.
144                    if (en.getValue().isEnabled() && !en.getValue().getUniDirection()) {
145                        if (en.getKey().getNXState() == EntryExitPairs.NXBUTTONSELECTED) {
146                            // Call activeBean via the Source object when bi-directional
147                            sourceRoute.activeBean(en.getValue(), true);
148                        }
149                    }
150                }
151            } else if (now == Sensor.INACTIVE && getNXState() == EntryExitPairs.NXBUTTONSELECTED) {
152                //sensor inactive, nxbutton state was selected, going to set back to inactive - ie user cancelled button
153                setButtonState(EntryExitPairs.NXBUTTONINACTIVE);
154            } else if (now == Sensor.INACTIVE && getNXState() == EntryExitPairs.NXBUTTONACTIVE) {
155                //Sensor gone inactive, while nxbutton was selected - potential start of user either clear route or setting another
156                setButtonState(EntryExitPairs.NXBUTTONSELECTED);
157                for (Entry<PointDetails, DestinationPoints> en : sourceRoute.pointToDest.entrySet()) {
158                    //Sensor sen = getSensorFromPoint(en.getKey().getPoint());
159                    //Set a time out on the source sensor, so that if its state hasn't been changed, then we will clear it out.
160                    if (en.getValue().isEnabled() && !en.getValue().getUniDirection()) {
161                        if (en.getKey().getNXState() == EntryExitPairs.NXBUTTONSELECTED) {
162                            // Call activeBean via the Source object when bi-directional
163                            sourceRoute.activeBean(en.getValue(), false);
164                        }
165                    }
166                }
167            }
168        } else if (destPoint != null) {
169            //Button set as a destination but has no source, it has had a change in state
170            if (now == Sensor.ACTIVE) {
171                //State now is Active will set flashing
172                setButtonState(EntryExitPairs.NXBUTTONSELECTED);
173            } else if (getNXState() == EntryExitPairs.NXBUTTONACTIVE) {
174                //Sensor gone inactive while it was previosly active
175                setButtonState(EntryExitPairs.NXBUTTONSELECTED);
176            } else if (getNXState() == EntryExitPairs.NXBUTTONSELECTED) {
177                //Sensor gone inactive while it was previously selected therefore will cancel
178                setButtonState(EntryExitPairs.NXBUTTONINACTIVE);
179            }
180        }
181
182        log.debug("[nxButtonStateChange] Initial button or possible multiple point route");
183        jmri.InstanceManager.getDefault(jmri.jmrit.entryexit.EntryExitPairs.class).setMultiPointRoute(this, panel);
184    }
185
186    void setSignalMast(SignalMast mast) {
187        signalmast = mast;
188    }
189
190    void setSource(Source src) {
191        if (sourceRoute == src) {
192            return;
193        }
194        sourceRoute = src;
195    }
196
197    void setDestination(DestinationPoints srcdp, Source src) {
198        if (!destinations.containsKey(srcdp)) {
199            destinations.put(srcdp, src);
200        }
201    }
202
203    void removeDestination(DestinationPoints srcdp) {
204        destinations.remove(srcdp);
205        if (sourceRoute == null && destinations.isEmpty()) {
206            stopFlashSensor();
207            sensor.removePropertyChangeListener(nxButtonListener);
208            setSensor(null);
209        }
210    }
211
212    void removeSource(Source src) {
213        sourceRoute = null;
214        if (destinations.isEmpty()) {
215            stopFlashSensor();
216            setSensor(null);
217        }
218    }
219
220    private int nxButtonState = EntryExitPairs.NXBUTTONINACTIVE;
221
222    void setButtonState(int state) {
223        setNXButtonState(state);
224    }
225
226    void setNXState(int state) {
227        if (state == nxButtonState) {
228            return;
229        }
230        if (state == EntryExitPairs.NXBUTTONSELECTED) {
231            nxButtonTimeOut();
232            flashSensor();
233        } else {
234            cancelNXButtonTimeOut();
235            stopFlashSensor();
236        }
237        nxButtonState = state;
238    }
239
240    public int getNXState() {
241        return nxButtonState;
242    }
243
244    SignalMast getSignalMast() {
245        return signalmast;
246    }
247
248    void setSignalHead(SignalHead head) {
249        signalhead = head;
250    }
251
252    SignalHead getSignalHead() {
253        return signalhead;
254    }
255
256    public LayoutEditor getPanel() {
257        return panel;
258    }
259
260    public void setRefObject(NamedBean refObs) {
261        for (LayoutEditor pnl : InstanceManager.getDefault(EditorManager.class).getAll(LayoutEditor.class)) {
262            if (refLoc == null) {
263                setRefObjectByPanel(refObs, pnl);
264            }
265        }
266    }
267
268    public void setRefObjectByPanel(NamedBean refObs, LayoutEditor pnl) {
269        refObj = refObs;
270        if (pnl != null && refObj != null) {
271            if (refObj instanceof SignalMast || refObj instanceof Sensor) {
272                //String mast = ((SignalMast)refObj).getUserName();
273                refLoc = pnl.getFinder().findPositionablePointByEastBoundBean(refObj);
274                if (refLoc == null) {
275                    refLoc = pnl.getFinder().findPositionablePointByWestBoundBean(refObj);
276                }
277                if (refLoc == null) {
278                    refLoc = pnl.getFinder().findLayoutTurnoutByBean(refObj);
279                }
280                if (refLoc == null) {
281                    refLoc = pnl.getFinder().findLevelXingByBean(refObj);
282                }
283                if (refLoc == null) {
284                    refLoc = pnl.getFinder().findLayoutSlipByBean(refObj);
285                }
286                if (refObj instanceof Sensor) {
287                    setSensor((Sensor) refObj);
288                }
289            } else if (refObj instanceof SignalHead) {
290                String signal = ((SignalHead) refObj).getDisplayName();
291                refLoc = pnl.getFinder().findPositionablePointByEastBoundSignal(signal);
292                if (refLoc == null) {
293                    refLoc = pnl.getFinder().findPositionablePointByWestBoundSignal(signal);
294                }
295            }
296        }
297//        if (refLoc != null) {
298//            if (refLoc instanceof PositionablePoint) {
299//                //((PositionablePoint)refLoc).addPropertyChangeListener(this);
300//            } else if (refLoc instanceof LayoutTurnout) {  //<== this includes LayoutSlips
301//                //((LayoutTurnout)refLoc).addPropertyChangeListener(this);
302//            } else if (refLoc instanceof LevelXing) {
303//                //((LevelXing)refLoc).addPropertyChangeListener(this);
304//            }
305//        }
306        //With this set ref we can probably add a listener to it, so that we can detect when a change to the point details takes place
307    }
308
309    public NamedBean getRefObject() {
310        return refObj;
311    }
312
313    public Object getRefLocation() {
314        return refLoc;
315    }
316
317    //LayoutEditor getLayoutEditor() { return panel; }
318    public boolean isRouteToPointSet() {
319        return routeToSet;
320    }
321
322    public boolean isRouteFromPointSet() {
323        return routeFromSet;
324    }
325
326    public String getDisplayName() {
327        if (sensor != null) {
328            String description = sensor.getDisplayName();
329            if (signalmast != null) {
330                description = description + " (" + signalmast.getDisplayName() + ")";
331            }
332            return description;
333        }
334
335        if (refObj instanceof SignalMast) {
336            return ((SignalMast) refObj).getDisplayName();
337        } else if (refObj instanceof Sensor) {
338            return ((Sensor) refObj).getDisplayName();
339        } else if (refObj instanceof SignalHead) {
340            return ((SignalHead) refObj).getDisplayName();
341        }
342        return "no display name";  // NOI18N
343    }
344
345    transient Thread nxButtonTimeOutThr;
346
347    void nxButtonTimeOut() {
348        if ((nxButtonTimeOutThr != null) && (nxButtonTimeOutThr.isAlive())) {
349            return;
350        }
351        extendedtime = true;
352        class ButtonTimeOut implements Runnable {
353
354            ButtonTimeOut() {
355            }
356
357            @Override
358            public void run() {
359                try {
360                    //Stage one default timer for the button if no other button has been pressed
361                    Thread.sleep(nxButtonTimeout * 1000L);
362                    //Stage two if an extended time out has been requested
363                    if (extendedtime) {
364                        Thread.sleep(60000);  //timeout after a minute waiting for the sml to set.
365                    }
366                } catch (InterruptedException ex) {
367                    log.debug("[nxButtonTimeOut] Flash timer cancelled");  // NOI18N
368                }
369                setNXButtonState(EntryExitPairs.NXBUTTONINACTIVE);
370            }
371        }
372        ButtonTimeOut t = new ButtonTimeOut();
373        nxButtonTimeOutThr = jmri.util.ThreadingUtil.newThread(t, "NX Button Timeout " + getSensor().getDisplayName());  // NOI18N
374
375        nxButtonTimeOutThr.start();
376    }
377
378    void cancelNXButtonTimeOut() {
379        if (nxButtonTimeOutThr != null) {
380            nxButtonTimeOutThr.interrupt();
381        }
382
383    }
384
385    boolean extendedtime = false;
386
387    public void flashSensor() {
388        for (SensorIcon si : getPanel().getSensorList()) {
389            if (si.getSensor() == getSensor()) {
390                si.flashSensor(2, Sensor.ACTIVE, Sensor.INACTIVE);
391            }
392        }
393    }
394
395    public void stopFlashSensor() {
396        for (SensorIcon si : getPanel().getSensorList()) {
397            if (si.getSensor() == getSensor()) {
398                si.stopFlash();
399            }
400        }
401    }
402
403    synchronized public void setNXButtonState(int state) {
404        if (getSensor() == null) {
405            return;
406        }
407        if (state == EntryExitPairs.NXBUTTONINACTIVE) {
408            //If a route is set to or from out point then we need to leave/set the sensor to ACTIVE
409            if (isRouteToPointSet()) {
410                state = EntryExitPairs.NXBUTTONACTIVE;
411            } else if (isRouteFromPointSet()) {
412                state = EntryExitPairs.NXBUTTONACTIVE;
413            }
414        }
415        setNXState(state);
416        int sensorState;
417        switch (state) {
418            case EntryExitPairs.NXBUTTONINACTIVE:
419                sensorState = Sensor.INACTIVE;
420                break;
421            case EntryExitPairs.NXBUTTONACTIVE:
422                sensorState = Sensor.ACTIVE;
423                break;
424            case EntryExitPairs.NXBUTTONSELECTED:
425                sensorState = Sensor.ACTIVE;
426                break;
427            default:
428                sensorState = Sensor.UNKNOWN;
429                break;
430        }
431
432        //Might need to clear listeners at the stage and then reapply them after.
433        if (getSensor().getKnownState() != sensorState) {
434            removeSensorList();
435            try {
436                getSensor().setKnownState(sensorState);
437            } catch (jmri.JmriException ex) {
438                log.error("Could not set Sensor known state.", ex);
439            }
440            addSensorList();
441        }
442    }
443
444    /**
445     * @since 4.17.6
446     * Making the source object available for scripting in Jython.
447     * @return the sensor.
448     */
449    public Sensor getSensor() {
450        if (getRefObject() == null) {
451            return null;
452        }
453        if ((getPanel() != null) && (!getPanel().isEditable()) && (sensor != null)) {
454            return sensor;
455        }
456
457        if (getRefObject() instanceof Sensor) {
458            setSensor((Sensor) getRefObject());
459            return (Sensor) getRefObject();
460        }
461        Object objLoc = getRefLocation();
462        Object objRef = getRefObject();
463        SignalHead head = null;
464        SignalMast mast = null;
465        String username = "";
466        String systemname = "";
467        Sensor foundSensor = null;
468        if (objRef instanceof SignalMast) {
469            mast = (SignalMast) objRef;
470        }
471        if (objRef instanceof SignalHead) {
472            head = (SignalHead) objRef;
473            username = head.getUserName();
474            systemname = head.getSystemName();
475        }
476        jmri.SensorManager sm = InstanceManager.sensorManagerInstance();
477        if (objLoc instanceof PositionablePoint) {
478            PositionablePoint p = (PositionablePoint) objLoc;
479            if (mast != null) {
480                if (p.getEastBoundSignalMast() == objRef) {
481                    foundSensor = p.getEastBoundSensor();
482                } else if (p.getWestBoundSignalMast() == objRef) {
483                    foundSensor = p.getWestBoundSensor();
484                }
485            } else if (head != null) {
486                if ((p.getEastBoundSignal().equals(username))
487                        || p.getEastBoundSignal().equals(systemname)) {
488                    foundSensor = p.getEastBoundSensor();
489                } else if ((p.getWestBoundSignal().equals(username))
490                        || p.getWestBoundSignal().equals(systemname)) {
491                    foundSensor = p.getWestBoundSensor();
492                }
493            }
494        } else if (objLoc instanceof LayoutSlip) {
495            LayoutSlip sl = (LayoutSlip) objLoc;
496            if (mast != null) {
497                if (sl.getSignalAMast() == objRef) {
498                    foundSensor = sl.getSensorA();
499                } else if (sl.getSignalBMast() == objRef) {
500                    foundSensor = sl.getSensorB();
501                } else if (sl.getSignalCMast() == objRef) {
502                    foundSensor = sl.getSensorC();
503                } else if (sl.getSignalDMast() == objRef) {
504                    foundSensor = sl.getSensorD();
505                }
506            }
507            if (head != null) {
508                if ((sl.getSignalA1Name().equals(username)) || (sl.getSignalA1Name().equals(systemname))) {
509                    foundSensor = sm.getSensor(sl.getSensorAName());
510                } else if ((sl.getSignalB1Name().equals(username)) || (sl.getSignalB1Name().equals(systemname))) {
511                    foundSensor = sm.getSensor(sl.getSensorBName());
512                } else if ((sl.getSignalC1Name().equals(username)) || (sl.getSignalC1Name().equals(systemname))) {
513                    foundSensor = sm.getSensor(sl.getSensorCName());
514                } else if ((sl.getSignalD1Name().equals(username)) || (sl.getSignalD1Name().equals(systemname))) {
515                    foundSensor = sm.getSensor(sl.getSensorDName());
516                }
517            }
518        } else //note: you have to do this after LayoutSlip
519        // because LayoutSlip extends LayoutTurnout
520        // (So a LayoutSlip would be an instance of LayoutTurnout.)
521        if (objLoc instanceof LayoutTurnout) {  //<== this includes LayoutSlips
522            LayoutTurnout t = (LayoutTurnout) objLoc;
523            if (mast != null) {
524                if (t.getSignalAMast() == objRef) {
525                    foundSensor = t.getSensorA();
526                } else if (t.getSignalBMast() == objRef) {
527                    foundSensor = t.getSensorB();
528                } else if (t.getSignalCMast() == objRef) {
529                    foundSensor = t.getSensorC();
530                } else if (t.getSignalDMast() == objRef) {
531                    foundSensor = t.getSensorD();
532                }
533            }
534            if (head != null) {
535                if ((t.getSignalA1Name().equals(username)) || (t.getSignalA1Name().equals(systemname))) {
536                    foundSensor = t.getSensorA();
537                } else if ((t.getSignalA2Name().equals(username)) || (t.getSignalA2Name().equals(systemname))) {
538                    foundSensor = t.getSensorA();
539                } else if ((t.getSignalA3Name().equals(username)) || (t.getSignalA3Name().equals(systemname))) {
540                    foundSensor = t.getSensorA();
541                } else if ((t.getSignalB1Name().equals(username)) || (t.getSignalB1Name().equals(systemname))) {
542                    foundSensor = t.getSensorB();
543                } else if ((t.getSignalB2Name().equals(username)) || (t.getSignalB2Name().equals(systemname))) {
544                    foundSensor = t.getSensorB();
545                } else if ((t.getSignalC1Name().equals(username)) || (t.getSignalC1Name().equals(systemname))) {
546                    foundSensor = t.getSensorC();
547                } else if ((t.getSignalC2Name().equals(username)) || (t.getSignalC2Name().equals(systemname))) {
548                    foundSensor = t.getSensorC();
549                } else if ((t.getSignalD1Name().equals(username)) || (t.getSignalD1Name().equals(systemname))) {
550                    foundSensor = t.getSensorD();
551                } else if ((t.getSignalD2Name().equals(username)) || (t.getSignalD2Name().equals(systemname))) {
552                    foundSensor = t.getSensorD();
553                }
554            }
555        } else if (objLoc instanceof LevelXing) {
556            LevelXing x = (LevelXing) objLoc;
557            if (mast != null) {
558                if (x.getSignalAMast() == objRef) {
559                    foundSensor = x.getSensorA();
560                } else if (x.getSignalBMast() == objRef) {
561                    foundSensor = x.getSensorB();
562                } else if (x.getSignalCMast() == objRef) {
563                    foundSensor = x.getSensorC();
564                } else if (x.getSignalDMast() == objRef) {
565                    foundSensor = x.getSensorD();
566                }
567            }
568            if (head != null) {
569                if ((x.getSignalAName().equals(username)) || (x.getSignalAName().equals(systemname))) {
570                    foundSensor = x.getSensorA();
571                } else if ((x.getSignalBName().equals(username)) || (x.getSignalBName().equals(systemname))) {
572                    foundSensor = x.getSensorB();
573                } else if ((x.getSignalCName().equals(username)) || (x.getSignalCName().equals(systemname))) {
574                    foundSensor = x.getSensorC();
575                } else if ((x.getSignalDName().equals(username)) || (x.getSignalDName().equals(systemname))) {
576                    foundSensor = x.getSensorD();
577                }
578            }
579        }
580        setSensor(foundSensor);
581        return foundSensor;
582    }
583
584    NamedBean getSignal() {
585        if ((getPanel() != null) && (!getPanel().isEditable()) && (getSignalMast() != null)) {
586            return getSignalMast();
587        }
588        if ((getPanel() != null) && (!getPanel().isEditable()) && (getSignalHead() != null)) {
589            return getSignalHead();
590        }
591        jmri.SignalHeadManager sh = InstanceManager.getDefault(jmri.SignalHeadManager.class);
592        NamedBean signal = null;
593
594        if (getRefObject() == null) {
595            log.error("Signal not found at point");  // NOI18N
596            return null;
597        } else if (getRefObject() instanceof SignalMast) {
598            signal = getRefObject();
599            setSignalMast(((SignalMast) getRefObject()));
600            return signal;
601        } else if (getRefObject() instanceof SignalHead) {
602            signal = getRefObject();
603            setSignalHead(((SignalHead) getRefObject()));
604            return signal;
605        }
606
607        Sensor sen = (Sensor) getRefObject();
608        log.debug("  Looking at sensor '{}' on panel '{}' at '{}'",
609                sen.getDisplayName(), getPanel().getLayoutName(), getRefLocation());
610        if (getRefLocation() instanceof PositionablePoint) {
611            PositionablePoint p = (PositionablePoint) getRefLocation();
612            if (p.getEastBoundSensor() == sen) {
613                if (p.getEastBoundSignalMast() != null) {
614                    signal = p.getEastBoundSignalMast();
615                } else if (!p.getEastBoundSignal().isEmpty()) {
616                    signal = sh.getSignalHead(p.getEastBoundSignal());
617                }
618            } else if (p.getWestBoundSensor() == sen) {
619                if (p.getWestBoundSignalMast() != null) {
620                    signal = p.getWestBoundSignalMast();
621                } else if (!p.getWestBoundSignal().isEmpty()) {
622                    signal = sh.getSignalHead(p.getWestBoundSignal());
623                }
624            }
625        } else if (getRefLocation() instanceof LayoutSlip) {
626            LayoutSlip t = (LayoutSlip) getRefLocation();
627            if (t.getSensorA() == sen) {
628                if (t.getSignalAMast() != null) {
629                    signal = t.getSignalAMast();
630                } else if (!t.getSignalA1Name().isEmpty()) {
631                    signal = sh.getSignalHead(t.getSignalA1Name());
632                }
633            } else if (t.getSensorB() == sen) {
634                if (t.getSignalBMast() != null) {
635                    signal = t.getSignalBMast();
636                } else if (!t.getSignalB1Name().isEmpty()) {
637                    signal = sh.getSignalHead(t.getSignalB1Name());
638                }
639            } else if (t.getSensorC() == sen) {
640                if (t.getSignalCMast() != null) {
641                    signal = t.getSignalCMast();
642                } else if (!t.getSignalC1Name().isEmpty()) {
643                    signal = sh.getSignalHead(t.getSignalC1Name());
644                }
645            } else if (t.getSensorD() == sen) {
646                if (t.getSignalDMast() != null) {
647                    signal = t.getSignalDMast();
648                } else if (!t.getSignalD1Name().isEmpty()) {
649                    signal = sh.getSignalHead(t.getSignalD1Name());
650                }
651            }
652        } else //note: you have to do this after LayoutSlip
653        // because LayoutSlip extends LayoutTurnout
654        // (So a LayoutSlip would be an instance of LayoutTurnout.)
655        if (getRefLocation() instanceof LayoutTurnout) {  //<== this includes LayoutSlips
656            LayoutTurnout t = (LayoutTurnout) getRefLocation();
657            if (t.getSensorA() == sen) {
658                if (t.getSignalAMast() != null) {
659                    signal = t.getSignalAMast();
660                } else if (!t.getSignalA1Name().isEmpty()) {
661                    signal = sh.getSignalHead(t.getSignalA1Name());
662                }
663            } else if (t.getSensorB() == sen) {
664                if (t.getSignalBMast() != null) {
665                    signal = t.getSignalBMast();
666                } else if (!t.getSignalB1Name().isEmpty()) {
667                    signal = sh.getSignalHead(t.getSignalB1Name());
668                }
669            } else if (t.getSensorC() == sen) {
670                if (t.getSignalCMast() != null) {
671                    signal = t.getSignalCMast();
672                } else if (!t.getSignalC1Name().isEmpty()) {
673                    signal = sh.getSignalHead(t.getSignalC1Name());
674                }
675            } else if (t.getSensorD() == sen) {
676                if (t.getSignalDMast() != null) {
677                    signal = t.getSignalDMast();
678                } else if (!t.getSignalD1Name().isEmpty()) {
679                    signal = sh.getSignalHead(t.getSignalD1Name());
680                }
681            }
682        } else if (getRefLocation() instanceof LevelXing) {
683            LevelXing x = (LevelXing) getRefLocation();
684            if (x.getSensorA() == sen) {
685                if (x.getSignalAMast() != null) {
686                    signal = x.getSignalAMast();
687                } else if (!x.getSignalAName().isEmpty()) {
688                    signal = sh.getSignalHead(x.getSignalAName());
689                }
690            } else if (x.getSensorB() == sen) {
691                if (x.getSignalBMast() != null) {
692                    signal = x.getSignalBMast();
693                } else if (!x.getSignalBName().isEmpty()) {
694                    signal = sh.getSignalHead(x.getSignalBName());
695                }
696            } else if (x.getSensorC() == sen) {
697                if (x.getSignalCMast() != null) {
698                    signal = x.getSignalCMast();
699                } else if (!x.getSignalCName().isEmpty()) {
700                    signal = sh.getSignalHead(x.getSignalCName());
701                }
702            } else if (x.getSensorD() == sen) {
703                if (x.getSignalDMast() != null) {
704                    signal = x.getSignalDMast();
705                } else if (!x.getSignalDName().isEmpty()) {
706                    signal = sh.getSignalHead(x.getSignalDName());
707                }
708            }
709        }
710
711        if (signal instanceof SignalMast) {
712            setSignalMast(((SignalMast) signal));
713        } else if (signal instanceof SignalHead) {
714            setSignalHead(((SignalHead) signal));
715        }
716        return signal;
717    }
718
719    @Override
720    public boolean equals(Object obj) {
721        if (obj == this) {
722            return true;
723        }
724        if (obj == null) {
725            return false;
726        }
727
728        if (!(getClass() == obj.getClass())) {
729            return false;
730        } else {
731            PointDetails tmp = (PointDetails) obj;
732            if (tmp.getFacing() != this.facing) {
733                return false;
734            }
735            if (!tmp.getProtecting().equals(this.protectingBlocks)) {
736                return false;
737            }
738            if (tmp.getPanel() != this.panel) {
739                return false;
740            }
741        }
742        return true;
743    }
744
745    @Override
746    public int hashCode() {
747        int hash = 7;
748        hash = 37 * hash + (this.panel != null ? this.panel.hashCode() : 0);
749        hash = 37 * hash + (this.facing != null ? this.facing.hashCode() : 0);
750        hash = 37 * hash + (this.protectingBlocks != null ? this.protectingBlocks.hashCode() : 0);
751        return hash;
752    }
753
754    java.beans.PropertyChangeSupport pcs = new java.beans.PropertyChangeSupport(this);
755
756    public synchronized void addPropertyChangeListener(java.beans.PropertyChangeListener l) {
757        pcs.addPropertyChangeListener(l);
758    }
759
760    public synchronized void removePropertyChangeListener(java.beans.PropertyChangeListener l) {
761        pcs.removePropertyChangeListener(l);
762    }
763
764    protected void firePropertyChange(String p, Object old, Object n) {
765        pcs.firePropertyChange(p, old, n);
766    }
767
768    private final static Logger log = LoggerFactory.getLogger(PointDetails.class);
769}