001package jmri.implementation;
002
003import java.beans.PropertyChangeEvent;
004import java.beans.PropertyChangeListener;
005import java.beans.PropertyVetoException;
006import java.beans.VetoableChangeListener;
007import java.util.*;
008
009import javax.annotation.Nonnull;
010
011import jmri.*;
012import jmri.jmrit.display.EditorManager;
013import jmri.jmrit.display.layoutEditor.ConnectivityUtil;
014import jmri.jmrit.display.layoutEditor.LayoutBlock;
015import jmri.jmrit.display.layoutEditor.LayoutBlockConnectivityTools;
016import jmri.jmrit.display.layoutEditor.LayoutBlockManager;
017import jmri.jmrit.display.layoutEditor.LayoutEditor;
018import jmri.jmrit.display.layoutEditor.LayoutSlip;
019import jmri.jmrit.display.layoutEditor.LayoutTrackExpectedState;
020import jmri.jmrit.display.layoutEditor.LayoutTurnout;
021import jmri.jmrit.display.layoutEditor.LayoutTurntable;
022import jmri.jmrit.display.layoutEditor.LevelXing;
023import jmri.util.ThreadingUtil;
024
025/**
026 * Default implementation of {@link jmri.SignalMastLogic}.
027 *
028 * @author Kevin Dickerson Copyright (C) 2011
029 */
030public class DefaultSignalMastLogic extends AbstractNamedBean implements SignalMastLogic, VetoableChangeListener {
031
032    SignalMast source;
033    SignalMast destination;
034    String stopAspect;
035
036    Hashtable<SignalMast, DestinationMast> destList = new Hashtable<>();
037    LayoutEditor editor;
038
039    LayoutBlock facingBlock = null;
040    LayoutBlock remoteProtectingBlock = null;
041
042    boolean disposing = false;
043
044    /**
045     * Initialise a Signal Mast Logic for a given source Signal Mast.
046     *
047     * @param source  The Signal Mast we are configuring an SML for
048     */
049    public DefaultSignalMastLogic(@Nonnull SignalMast source) {
050        super(source.toString()); // default system name
051        this.source = source;
052        try {
053            this.stopAspect = source.getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.DANGER);
054            this.source.addPropertyChangeListener(propertySourceMastListener);
055            if (source.getAspect() == null) {
056                source.setAspect(stopAspect);
057            }
058        } catch (Exception ex) {
059            log.error("Error while creating Signal Logic", ex);
060        }
061    }
062
063    // Most of the following methods will inherit Javadoc from SignalMastLogic.java
064    /**
065     * {@inheritDoc }
066     */
067    @Override
068    public void setFacingBlock(LayoutBlock facing) {
069        facingBlock = facing;
070    }
071
072    /**
073     * {@inheritDoc }
074     */
075    @Override
076    public LayoutBlock getFacingBlock() {
077        return facingBlock;
078    }
079
080    /**
081     * {@inheritDoc }
082     */
083    @Override
084    public LayoutBlock getProtectingBlock(@Nonnull SignalMast dest) {
085        if (!destList.containsKey(dest)) {
086            return null;
087        }
088        return destList.get(dest).getProtectingBlock();
089    }
090
091    /**
092     * {@inheritDoc }
093     */
094    @Override
095    public SignalMast getSourceMast() {
096        return source;
097    }
098
099    /**
100     * {@inheritDoc }
101     */
102    @Override
103    public void replaceSourceMast(SignalMast oldMast, SignalMast newMast) {
104        if (oldMast != source) {
105            // Old mast does not match new mast so will exit replace
106            return;
107        }
108        source.removePropertyChangeListener(propertySourceMastListener);
109        source = newMast;
110        stopAspect = source.getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.DANGER);
111        source.addPropertyChangeListener(propertySourceMastListener);
112        if (source.getAspect() == null) {
113            source.setAspect(stopAspect);
114        }
115        getDestinationList().forEach(sm -> {
116            DestinationMast destMast = destList.get(sm);
117            if (destMast.getAssociatedSection() != null) {
118                String oldUserName = destMast.getAssociatedSection().getUserName();
119                String newUserName = source.getDisplayName() + ":" + sm.getDisplayName();
120                if (oldUserName != null) {
121                    InstanceManager.getDefault(NamedBeanHandleManager.class)
122                        .renameBean(oldUserName, newUserName, ((NamedBean) destMast.getAssociatedSection()));
123                } else {
124                    log.warn("AssociatedSection oldUserName null for destination mast {}, skipped",
125                        destMast.getDisplayName());
126                }
127            }
128        });
129        firePropertyChange(PROPERTY_UPDATED_SOURCE, oldMast, newMast);
130    }
131
132    /**
133     * {@inheritDoc }
134     */
135    @Override
136    public void replaceDestinationMast(SignalMast oldMast, SignalMast newMast) {
137        if (!destList.containsKey(oldMast)) {
138            return;
139        }
140        DestinationMast destMast = destList.get(oldMast);
141        destMast.updateDestinationMast(newMast);
142        if (destination == oldMast) {
143            oldMast.removePropertyChangeListener(propertyDestinationMastListener);
144            newMast.addPropertyChangeListener(propertyDestinationMastListener);
145            destination = newMast;
146            setSignalAppearance();
147        }
148        destList.remove(oldMast);
149        if (destMast.getAssociatedSection() != null) {
150            String oldUserName = destMast.getAssociatedSection().getUserName();
151            String newUserName = source.getDisplayName() + ":" + newMast.getDisplayName();
152            if (oldUserName != null) {
153                InstanceManager.getDefault(NamedBeanHandleManager.class)
154                    .renameBean(oldUserName, newUserName, destMast.getAssociatedSection());
155            } else {
156                log.warn("AssociatedSection oldUserName null for destination mast {}, skipped",
157                    destMast.getDisplayName());
158            }
159        }
160        destList.put(newMast, destMast);
161        firePropertyChange(PROPERTY_UPDATED_DESTINATION, oldMast, newMast);
162    }
163
164    /**
165     * {@inheritDoc }
166     */
167    @Override
168    public void setDestinationMast(SignalMast dest) {
169        if (destList.containsKey(dest)) {
170            // if already present, not a change
171            log.debug("Destination mast '{}' was already defined in SML with this source mast", dest.getDisplayName());
172            return;
173        }
174        int oldSize = destList.size();
175        destList.put(dest, new DestinationMast(dest));
176        //InstanceManager.getDefault(SignalMastLogicManager.class).addDestinationMastToLogic(this, dest);
177        firePropertyChange(PROPERTY_LENGTH, oldSize, destList.size());
178        // make new dest mast appear in (update of) SignallingSourcePanel Table by having that table listen to PropertyChange Events from SML TODO
179    }
180
181    /**
182     * {@inheritDoc }
183     */
184    @Override
185    public boolean isDestinationValid(SignalMast dest) {
186        if (dest == null) {
187            return false;
188        }
189        return destList.containsKey(dest);
190    }
191
192    /**
193     * {@inheritDoc }
194     */
195    @Override
196    public List<SignalMast> getDestinationList() {
197        List<SignalMast> out = new ArrayList<>();
198        Enumeration<SignalMast> en = destList.keys();
199        while (en.hasMoreElements()) {
200            out.add(en.nextElement());
201        }
202        return out;
203    }
204
205    /**
206     * {@inheritDoc }
207     */
208    @Override
209    public String getComment(SignalMast dest) {
210        if (!destList.containsKey(dest)) {
211            return "";
212        }
213        return destList.get(dest).getComment();
214    }
215
216    /**
217     * {@inheritDoc }
218     */
219    @Override
220    public void setComment(String comment, SignalMast dest) {
221        if (!destList.containsKey(dest)) {
222            return;
223        }
224        destList.get(dest).setComment(comment);
225    }
226
227    /**
228     * {@inheritDoc }
229     */
230    @Override
231    public void setStore(int store, SignalMast destination) {
232        if (!destList.containsKey(destination)) {
233            return;
234        }
235        destList.get(destination).setStore(store);
236    }
237
238    /**
239     * {@inheritDoc }
240     */
241    @Override
242    public int getStoreState(SignalMast destination) {
243        if (!destList.containsKey(destination)) {
244            return STORENONE;
245        }
246        return destList.get(destination).getStoreState();
247    }
248
249    /**
250     * {@inheritDoc }
251     */
252    @Override
253    public void setEnabled(SignalMast dest) {
254        if (!destList.containsKey(dest)) {
255            return;
256        }
257        destList.get(dest).setEnabled();
258    }
259
260    /**
261     * {@inheritDoc }
262     */
263    @Override
264    public void setDisabled(SignalMast dest) {
265        if (!destList.containsKey(dest)) {
266            return;
267        }
268        destList.get(dest).setDisabled();
269    }
270
271    /**
272     * {@inheritDoc }
273     */
274    @Override
275    public boolean isEnabled(SignalMast dest) {
276        if (!destList.containsKey(dest)) {
277            return false;
278        }
279        return destList.get(dest).isEnabled();
280    }
281
282    /**
283     * {@inheritDoc }
284     */
285    @Override
286    public boolean isActive(SignalMast dest) {
287        if (!destList.containsKey(dest)) {
288            return false;
289        }
290        return destList.get(dest).isActive();
291    }
292
293    /**
294     * {@inheritDoc }
295     */
296    @Override
297    public SignalMast getActiveDestination() {
298        for (SignalMast sm : getDestinationList()) {
299            if (destList.get(sm).isActive()) {
300                return sm;
301            }
302        }
303        return null;
304    }
305
306    /**
307     * {@inheritDoc }
308     */
309    @Override
310    public boolean removeDestination(SignalMast dest) {
311        int oldSize = destList.size();
312        if (destList.containsKey(dest)) {
313            //InstanceManager.getDefault(SignalMastLogicManager.class).removeDestinationMastToLogic(this, dest);
314            destList.get(dest).dispose();
315            destList.remove(dest);
316            firePropertyChange(PROPERTY_LENGTH, oldSize, destList.size());
317        }
318        return destList.isEmpty();
319    }
320
321    /**
322     * {@inheritDoc }
323     */
324    @Override
325    public void disableLayoutEditorUse() {
326        destList.values().forEach(dest -> {
327            try {
328                dest.useLayoutEditor(false);
329            } catch (JmriException e) {
330                log.error("Could not disable LayoutEditor ",  e);
331            }
332        });
333    }
334
335    /**
336     * {@inheritDoc }
337     */
338    @Override
339    public void useLayoutEditor(boolean boo, SignalMast destination) throws JmriException {
340        if (!destList.containsKey(destination)) {
341            return;
342        }
343        if (boo) {
344            log.debug("Set use layout editor");
345            Set<LayoutEditor> layout = InstanceManager.getDefault(EditorManager.class).getAll(LayoutEditor.class);
346            /*We don't care which layout editor panel the signalmast is on, just so long as
347             the routing is done via layout blocks*/
348            // TODO: what is this?
349            log.debug("userLayoutEditor finds layout list size is {}", layout.size());
350            for (LayoutEditor findeditor : layout) {
351                log.debug("layouteditor {}", findeditor.getLayoutName());
352                if (facingBlock == null) {
353                    facingBlock = InstanceManager.getDefault(LayoutBlockManager.class)
354                        .getFacingBlockByMast(getSourceMast(), findeditor);
355                }
356            }
357        }
358        destList.get(destination).useLayoutEditor(boo);
359    }
360
361    /**
362     * Add direction sensors to SML
363     *
364     * @return number of errors
365     */
366    @Override
367    public int setupDirectionSensors() {
368        // iterrate over the signal masts
369        int errorCount = 0;
370        for (SignalMast sm : getDestinationList()) {
371            String displayName = sm.getDisplayName();
372            Section sec = getAssociatedSection(sm);
373            if (sec != null) {
374                Block thisFacingBlock;
375                Sensor fwd = sec.getForwardBlockingSensor();
376                Sensor rev = sec.getReverseBlockingSensor();
377                LayoutBlock lBlock = getFacingBlock();
378                if (lBlock == null) {
379                    try {
380                        useLayoutEditor(true, sm); // force a refind
381                    } catch (JmriException ex) {
382                        continue;
383                    }
384                }
385                if (lBlock != null) {
386                    thisFacingBlock = lBlock.getBlock();
387                    EntryPoint fwdEntryPoint = sec.getEntryPointFromBlock(thisFacingBlock, Section.FORWARD);
388                    EntryPoint revEntryPoint = sec.getEntryPointFromBlock(thisFacingBlock, Section.REVERSE);
389                    log.debug("Mast[{}] Sec[{}] Fwd[{}] Rev [{}]",
390                            displayName, sec, fwd, rev);
391                    if (fwd != null && fwdEntryPoint != null) {
392                        addSensor(fwd.getUserName(), Sensor.INACTIVE, sm);
393                        log.debug("Mast[{}] Sec[{}] Fwd[{}] fwdEP[{}]",
394                                displayName, sec, fwd,
395                                fwdEntryPoint.getBlock().getUserName());
396
397                    } else if (rev != null && revEntryPoint != null) {
398                        addSensor(rev.getUserName(), Sensor.INACTIVE, sm);
399                        log.debug("Mast[{}] Sec[{}] Rev [{}] revEP[{}]",
400                                displayName, sec, rev,
401                                revEntryPoint.getBlock().getUserName());
402
403                    } else {
404                        log.error("Mast[{}] Cannot Establish entry point to protected section", displayName);
405                        errorCount += 1;
406                    }
407                } else {
408                    log.error("Mast[{}] No Facing Block", displayName);
409                    errorCount += 1;
410                }
411            } else {
412                log.error("Mast[{}] No Associated Section", displayName);
413                errorCount += 1;
414            }
415        }
416        return errorCount;
417    }
418
419    /**
420     * {@inheritDoc }
421     */
422    @Override
423    public void removeDirectionSensors() {
424        //TODO find aaway of easilty identifying the ones we added.
425    }
426
427    /**
428     * {@inheritDoc }
429     */
430    @Override
431    public boolean useLayoutEditor(SignalMast destination) {
432        if (!destList.containsKey(destination)) {
433            return false;
434        }
435        return destList.get(destination).useLayoutEditor();
436    }
437
438    /**
439     * {@inheritDoc }
440     */
441    @Override
442    public void useLayoutEditorDetails(boolean turnouts, boolean blocks, SignalMast destination) throws JmriException {
443        if (!destList.containsKey(destination)) {
444            return;
445        }
446        destList.get(destination).useLayoutEditorDetails(turnouts, blocks);
447    }
448
449    /**
450     * {@inheritDoc }
451     */
452    @Override
453    public boolean useLayoutEditorBlocks(SignalMast destination) {
454        if (!destList.containsKey(destination)) {
455            return false;
456        }
457        return destList.get(destination).useLayoutEditorBlocks();
458    }
459
460    /**
461     * {@inheritDoc }
462     */
463    @Override
464    public boolean useLayoutEditorTurnouts(SignalMast destination) {
465        if (!destList.containsKey(destination)) {
466            return false;
467        }
468        return destList.get(destination).useLayoutEditorTurnouts();
469    }
470
471    /**
472     * {@inheritDoc }
473     */
474    @Override
475    public Section getAssociatedSection(SignalMast destination) {
476        if (!destList.containsKey(destination)) {
477            return null;
478        }
479        return destList.get(destination).getAssociatedSection();
480    }
481
482    /**
483     * {@inheritDoc }
484     */
485    @Override
486    public void setAssociatedSection(Section sec, SignalMast destination) {
487        if (!destList.containsKey(destination)) {
488            return;
489        }
490        destList.get(destination).setAssociatedSection(sec);
491    }
492
493    /**
494     * {@inheritDoc }
495     */
496    @Override
497    public boolean allowAutoMaticSignalMastGeneration(SignalMast destination) {
498        if (!destList.containsKey(destination)) {
499            return false;
500        }
501        return destList.get(destination).allowAutoSignalMastGen();
502    }
503
504    /**
505     * {@inheritDoc }
506     */
507    @Override
508    public void allowAutoMaticSignalMastGeneration(boolean allow, SignalMast destination) {
509        if (!destList.containsKey(destination)) {
510            return;
511        }
512        destList.get(destination).allowAutoSignalMastGen(allow);
513    }
514
515    /**
516     * {@inheritDoc }
517     */
518    @Override
519    public void allowTurnoutLock(boolean lock, SignalMast destination) {
520        if (!destList.containsKey(destination)) {
521            return;
522        }
523        destList.get(destination).allowTurnoutLock(lock);
524    }
525
526    /**
527     * {@inheritDoc }
528     */
529    @Override
530    public boolean isTurnoutLockAllowed(SignalMast destination) {
531        if (!destList.containsKey(destination)) {
532            return false;
533        }
534        return destList.get(destination).isTurnoutLockAllowed();
535    }
536
537    /**
538     * {@inheritDoc }
539     */
540    @Override
541    public void setTurnouts(Hashtable<NamedBeanHandle<Turnout>, Integer> turnouts, SignalMast destination) {
542        if (!destList.containsKey(destination)) {
543            return;
544        }
545        destList.get(destination).setTurnouts(turnouts);
546    }
547
548    /**
549     * {@inheritDoc }
550     */
551    @Override
552    public void setAutoTurnouts(Hashtable<Turnout, Integer> turnouts, SignalMast destination) {
553        if (!destList.containsKey(destination)) {
554            return;
555        }
556        destList.get(destination).setAutoTurnouts(turnouts);
557    }
558
559    /**
560     * {@inheritDoc }
561     */
562    @Override
563    public void setBlocks(Hashtable<Block, Integer> blocks, SignalMast destination) {
564        if (!destList.containsKey(destination)) {
565            return;
566        }
567        destList.get(destination).setBlocks(blocks);
568    }
569
570    /**
571     * {@inheritDoc }
572     */
573    @Override
574    public void setAutoBlocks(LinkedHashMap<Block, Integer> blocks, SignalMast destination) {
575        if (!destList.containsKey(destination)) {
576            return;
577        }
578        destList.get(destination).setAutoBlocks(blocks);
579    }
580
581    /**
582     * {@inheritDoc }
583     */
584    @Override
585    public void setMasts(Hashtable<SignalMast, String> masts, SignalMast destination) {
586        if (!destList.containsKey(destination)) {
587            return;
588        }
589        destList.get(destination).setMasts(masts);
590    }
591
592    /**
593     * {@inheritDoc }
594     */
595    @Override
596    public void setAutoMasts(Hashtable<SignalMast, String> masts, SignalMast destination) {
597        if (!destList.containsKey(destination)) {
598            return;
599        }
600        destList.get(destination).setAutoMasts(masts, true);
601    }
602
603    /**
604     * {@inheritDoc }
605     */
606    @Override
607    public void setSensors(Hashtable<NamedBeanHandle<Sensor>, Integer> sensors, SignalMast destination) {
608        if (!destList.containsKey(destination)) {
609            return;
610        }
611        destList.get(destination).setSensors(sensors);
612    }
613
614    /**
615     * {@inheritDoc }
616     */
617    @Override
618    public void addSensor(String sensorName, int state, SignalMast destination) {
619        if (!destList.containsKey(destination)) {
620            return;
621        }
622        Sensor sen = InstanceManager.sensorManagerInstance().getSensor(sensorName);
623        if (sen != null) {
624            NamedBeanHandle<Sensor> namedSensor = InstanceManager.getDefault(NamedBeanHandleManager.class)
625                .getNamedBeanHandle(sensorName, sen);
626            destList.get(destination).addSensor(namedSensor, state);
627        }
628    }
629
630    /**
631     * {@inheritDoc }
632     */
633    @Override
634    public void removeSensor(String sensorName, SignalMast destination) {
635        Sensor sen = InstanceManager.sensorManagerInstance().getSensor(sensorName);
636        removeSensor(sen, destination);
637    }
638
639    public void removeSensor(Sensor sen, SignalMast destination) {
640        if (!destList.containsKey(destination)) {
641            return;
642        }
643        if (sen != null) {
644            destList.get(destination).removeSensor(sen);
645        }
646    }
647
648    /**
649     * {@inheritDoc }
650     */
651    @Override
652    public List<Block> getBlocks(SignalMast destination) {
653        if (!destList.containsKey(destination)) {
654            return new ArrayList<>();
655        }
656        return destList.get(destination).getBlocks();
657    }
658
659    /**
660     * {@inheritDoc }
661     */
662    @Override
663    public List<Block> getAutoBlocks(SignalMast destination) {
664        if (!destList.containsKey(destination)) {
665            return new ArrayList<>();
666        }
667        return destList.get(destination).getAutoBlocks();
668    }
669
670    /**
671     * {@inheritDoc }
672     */
673    @Override
674    public List<Block> getAutoBlocksBetweenMasts(SignalMast destination) {
675        if (!destList.containsKey(destination)) {
676            return new ArrayList<>();
677        }
678        return destList.get(destination).getAutoBlocksBetweenMasts();
679    }
680
681    /**
682     * {@inheritDoc }
683     */
684    @Override
685    public List<Turnout> getTurnouts(SignalMast destination) {
686        if (!destList.containsKey(destination)) {
687            return new ArrayList<>();
688        }
689        return destList.get(destination).getTurnouts();
690    }
691
692    /**
693     * {@inheritDoc }
694     */
695    @Override
696    public List<NamedBeanHandle<Turnout>> getNamedTurnouts(SignalMast destination) {
697        if (!destList.containsKey(destination)) {
698            return new ArrayList<>();
699        }
700        return destList.get(destination).getNamedTurnouts();
701    }
702
703    public void removeTurnout(Turnout turn, SignalMast destination) {
704        if (!destList.containsKey(destination)) {
705            return;
706        }
707
708        if (turn != null) {
709            destList.get(destination).removeTurnout(turn);
710        }
711    }
712
713    /**
714     * {@inheritDoc }
715     */
716    @Override
717    public List<Turnout> getAutoTurnouts(SignalMast destination) {
718        if (!destList.containsKey(destination)) {
719            return new ArrayList<>();
720        }
721        return destList.get(destination).getAutoTurnouts();
722    }
723
724    /**
725     * {@inheritDoc }
726     */
727    @Override
728    public List<Sensor> getSensors(SignalMast destination) {
729        if (!destList.containsKey(destination)) {
730            return new ArrayList<>();
731        }
732        return destList.get(destination).getSensors();
733    }
734
735    /**
736     * {@inheritDoc }
737     */
738    @Override
739    public List<NamedBeanHandle<Sensor>> getNamedSensors(SignalMast destination) {
740        if (!destList.containsKey(destination)) {
741            return new ArrayList<>();
742        }
743        return destList.get(destination).getNamedSensors();
744    }
745
746    /**
747     * {@inheritDoc }
748     */
749    @Override
750    public List<SignalMast> getSignalMasts(SignalMast destination) {
751        if (!destList.containsKey(destination)) {
752            return new ArrayList<>();
753        }
754        return destList.get(destination).getSignalMasts();
755    }
756
757    /**
758     * {@inheritDoc }
759     */
760    @Override
761    public List<SignalMast> getAutoMasts(SignalMast destination) {
762        if (!destList.containsKey(destination)) {
763            return new ArrayList<>();
764        }
765        return destList.get(destination).getAutoSignalMasts();
766    }
767
768    /**
769     * {@inheritDoc }
770     */
771    @Override
772    public void initialise() {
773        Enumeration<SignalMast> en = destList.keys();
774        while (en.hasMoreElements()) {
775            destList.get(en.nextElement()).initialise();
776        }
777    }
778
779    /**
780     * {@inheritDoc }
781     */
782    @Override
783    public void initialise(SignalMast destination) {
784        if (disposing) {
785            return;
786        }
787
788        if (!destList.containsKey(destination)) {
789            return;
790        }
791        destList.get(destination).initialise();
792    }
793
794    /**
795     * {@inheritDoc }
796     */
797    @Override
798    public LinkedHashMap<Block, Integer> setupLayoutEditorTurnoutDetails(List<LayoutBlock> blks, SignalMast destination) {
799        if (disposing) {
800            return new LinkedHashMap<>();
801        }
802
803        if (!destList.containsKey(destination)) {
804            return new LinkedHashMap<>();
805        }
806        return destList.get(destination).setupLayoutEditorTurnoutDetails(blks);
807    }
808
809    /**
810     * {@inheritDoc }
811     */
812    @Override
813    public void setupLayoutEditorDetails() {
814        if (disposing) {
815            return;
816        }
817        Enumeration<SignalMast> en = destList.keys();
818        while (en.hasMoreElements()) {
819            try {
820                destList.get(en.nextElement()).setupLayoutEditorDetails();
821            } catch (JmriException e) {
822                //Considered normal if no route is valid on a Layout Editor panel
823            }
824        }
825    }
826
827    /**
828     * Check if routes to the destination Signal Mast are clear.
829     *
830     * @return true if the path to the next signal is clear
831     */
832    boolean checkStates() {
833        SignalMast oldActiveMast = destination;
834        if (destination != null) {
835            firePropertyChange(PROPERTY_STATE, oldActiveMast, null);
836            log.debug("Remove listener from destination");
837            destination.removePropertyChangeListener(propertyDestinationMastListener);
838            if (destList.containsKey(destination)) {
839                destList.get(destination).clearTurnoutLock();
840            }
841        }
842
843        Enumeration<SignalMast> en = destList.keys();
844        log.debug("checkStates enumerates over {} mast(s)", destList.size());
845        while (en.hasMoreElements()) {
846            SignalMast key = en.nextElement();
847            log.debug("  Destination mast {}", key.getDisplayName());
848            log.debug("    isEnabled: {}", (destList.get(key)).isEnabled());
849            log.debug("    isActive: {}", destList.get(key).isActive());
850
851            if ((destList.get(key)).isEnabled() && (destList.get(key).isActive())) {
852                destination = key;
853                log.debug("      Add listener to destination");
854                destination.addPropertyChangeListener(propertyDestinationMastListener);
855                log.debug("      firePropertyChange: \"state\"");
856                firePropertyChange(PROPERTY_STATE, oldActiveMast, destination);
857                destList.get(key).lockTurnouts();
858                return true;
859            }
860        }
861        return false;
862    }
863
864    /**
865     * {@inheritDoc }
866     */
867    @Override
868    public boolean areBlocksIncluded(List<Block> blks) {
869        Enumeration<SignalMast> en = destList.keys();
870        while (en.hasMoreElements()) {
871            SignalMast dm = en.nextElement();
872            boolean included;
873            for (int i = 0; i < blks.size(); i++) {
874                included = destList.get(dm).isBlockIncluded(blks.get(i));
875                if (included) {
876                    return true;
877                }
878                included = destList.get(dm).isAutoBlockIncluded(blks.get(i));
879                if (included) {
880                    return true;
881                }
882            }
883        }
884        return false;
885    }
886
887    /**
888     * {@inheritDoc }
889     */
890    @Override
891    public int getBlockState(Block block, SignalMast destination) {
892        if (!destList.containsKey(destination)) {
893            return -1;
894        }
895        return destList.get(destination).getBlockState(block);
896    }
897
898    /**
899     * {@inheritDoc }
900     */
901    @Override
902    public boolean isBlockIncluded(Block block, SignalMast destination) {
903        if (!destList.containsKey(destination)) {
904            return false;
905        }
906        return destList.get(destination).isBlockIncluded(block);
907    }
908
909    /**
910     * {@inheritDoc }
911     */
912    @Override
913    public boolean isTurnoutIncluded(Turnout turnout, SignalMast destination) {
914        if (!destList.containsKey(destination)) {
915            return false;
916        }
917        return destList.get(destination).isTurnoutIncluded(turnout);
918    }
919
920    /**
921     * {@inheritDoc }
922     */
923    @Override
924    public boolean isSensorIncluded(Sensor sensor, SignalMast destination) {
925        if (!destList.containsKey(destination)) {
926            return false;
927        }
928        return destList.get(destination).isSensorIncluded(sensor);
929    }
930
931    /**
932     * {@inheritDoc }
933     */
934    @Override
935    public boolean isSignalMastIncluded(SignalMast signal, SignalMast destination) {
936        if (!destList.containsKey(destination)) {
937            return false;
938        }
939        return destList.get(destination).isSignalMastIncluded(signal);
940    }
941
942    /**
943     * {@inheritDoc }
944     */
945    @Override
946    public int getAutoBlockState(Block block, SignalMast destination) {
947        if (!destList.containsKey(destination)) {
948            return -1;
949        }
950        return destList.get(destination).getAutoBlockState(block);
951    }
952
953    /**
954     * {@inheritDoc }
955     */
956    @Override
957    public int getSensorState(Sensor sensor, SignalMast destination) {
958        if (!destList.containsKey(destination)) {
959            return -1;
960        }
961        return destList.get(destination).getSensorState(sensor);
962    }
963
964    /**
965     * {@inheritDoc }
966     */
967    @Override
968    public int getTurnoutState(Turnout turnout, SignalMast destination) {
969        if (!destList.containsKey(destination)) {
970            return -1;
971        }
972        return destList.get(destination).getTurnoutState(turnout);
973    }
974
975    /**
976     * {@inheritDoc }
977     */
978    @Override
979    public int getAutoTurnoutState(Turnout turnout, SignalMast destination) {
980        if (!destList.containsKey(destination)) {
981            return -1;
982        }
983        return destList.get(destination).getAutoTurnoutState(turnout);
984    }
985
986    /**
987     * {@inheritDoc }
988     */
989    @Override
990    public String getSignalMastState(SignalMast mast, SignalMast destination) {
991        if (!destList.containsKey(destination)) {
992            return null;
993        }
994        return destList.get(destination).getSignalMastState(mast);
995    }
996
997    /**
998     * {@inheritDoc }
999     */
1000    @Override
1001    public String getAutoSignalMastState(SignalMast mast, SignalMast destination) {
1002        if (!destList.containsKey(destination)) {
1003            return null;
1004        }
1005        return destList.get(destination).getAutoSignalMastState(mast);
1006    }
1007
1008    /**
1009     * {@inheritDoc }
1010     */
1011    @Override
1012    public float getMaximumSpeed(SignalMast destination) {
1013        if (!destList.containsKey(destination)) {
1014            return -1;
1015        }
1016        return destList.get(destination).getMinimumSpeed();
1017    }
1018
1019    volatile boolean inWait = false;
1020
1021    /**
1022     * Before going active or checking that we can go active, wait 500ms
1023     * for things to settle down to help prevent a race condition.
1024     */
1025    synchronized void setSignalAppearance() {
1026        log.debug("setMastAppearance (Aspect) called for {}", source.getDisplayName());
1027        if (inWait) {
1028            log.debug("setMastAppearance (Aspect) called with inWait set, returning");
1029            return;
1030        }
1031        inWait = true;
1032
1033        // The next line forces a single initialization of InstanceManager.getDefault(SignalSpeedMap.class)
1034        // before launching parallel threads
1035        InstanceManager.getDefault(SignalSpeedMap.class);
1036
1037        // The next line forces a single initialization of InstanceManager.getDefault(SignalMastLogicManager.class)
1038        // before launching delay
1039        int tempDelay = InstanceManager.getDefault(SignalMastLogicManager.class).getSignalLogicDelay() / 2;
1040        log.debug("SignalMastLogicManager started (delay)");
1041        ThreadingUtil.runOnLayoutDelayed(
1042                () -> {
1043                    setMastAppearance();
1044                    inWait = false;
1045                },
1046                tempDelay
1047        );
1048
1049    }
1050
1051    /**
1052     * Evaluate the destination signal mast Aspect and set ours accordingly.
1053     */
1054    void setMastAppearance() {
1055        log.debug("Set source Signal Mast Aspect");
1056        if (getSourceMast().getHeld()) {
1057            log.debug("Signal is at a Held state so will set to the aspect defined for Held or Danger");
1058
1059            String heldAspect = getSourceMast().getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.HELD);
1060            if (heldAspect != null) {
1061                log.debug("  Setting to HELD value of {}", heldAspect);
1062                ThreadingUtil.runOnLayout(() -> {
1063                    getSourceMast().setAspect(heldAspect);
1064                });
1065            } else {
1066                String dangerAspect = getSourceMast().getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.DANGER);
1067                log.debug("  Setting to DANGER value of {}", dangerAspect);
1068                ThreadingUtil.runOnLayout(() -> {
1069                    getSourceMast().setAspect(dangerAspect);
1070                });
1071            }
1072            return;
1073        }
1074        if (!checkStates()) {
1075            log.debug("Advanced routes not clear, set Stop aspect");
1076            getSourceMast().setAspect(stopAspect);
1077            return;
1078        }
1079        String[] advancedAspect;
1080        if (destination.getHeld()) {
1081            if (destination.getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.HELD) != null) {
1082                advancedAspect = getSourceMast().getAppearanceMap().getValidAspectsForAdvancedAspect(destination.getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.HELD));
1083            } else {
1084                advancedAspect = getSourceMast().getAppearanceMap().getValidAspectsForAdvancedAspect(destination.getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.DANGER));
1085            }
1086        } else {
1087            advancedAspect = getSourceMast().getAppearanceMap().getValidAspectsForAdvancedAspect(destination.getAspect());
1088        }
1089
1090        log.debug("distant aspect is {}", destination.getAspect());
1091        log.debug("advanced aspect is {}", advancedAspect != null ? advancedAspect : "<null>");
1092
1093        if (advancedAspect != null) {
1094            String aspect = stopAspect;
1095            if (destList.get(destination).permissiveBlock) {
1096                if (!getSourceMast().isPermissiveSmlDisabled()) {
1097                    //if a block is in a permissive state then we set the permissive appearance
1098                    aspect = getSourceMast().getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.PERMISSIVE);
1099                }
1100            } else {
1101                for (String advancedAspect1 : advancedAspect) {
1102                    if (!getSourceMast().isAspectDisabled(advancedAspect1)) {
1103                        aspect = advancedAspect1;
1104                        break;
1105                    }
1106                }
1107                List<Integer> divergAspects = new ArrayList<>();
1108                List<Integer> nonDivergAspects = new ArrayList<>();
1109                List<Integer> eitherAspects = new ArrayList<>();
1110                if (advancedAspect.length > 1) {
1111                    float maxSigSpeed = -1;
1112                    float maxPathSpeed = destList.get(destination).getMinimumSpeed();
1113                    boolean divergRoute = destList.get(destination).turnoutThrown;
1114
1115                    log.debug("Diverging route? {}", divergRoute);
1116                    boolean divergFlagsAvailable = false;
1117                    //We split the aspects into two lists, one with diverging flag set, the other without.
1118                    for (int i = 0; i < advancedAspect.length; i++) {
1119                        String div = null;
1120                        if (!getSourceMast().isAspectDisabled(advancedAspect[i])) {
1121                            div = (String) getSourceMast().getSignalSystem().getProperty(advancedAspect[i], "route");
1122                        }
1123                        if (div != null) {
1124                            if (div.equals("Diverging")) {
1125                                log.debug("Aspect {} added as Diverging Route", advancedAspect[i]);
1126                                divergAspects.add(i);
1127                                divergFlagsAvailable = true;
1128                                log.debug("Using Diverging Flag");
1129                            } else if (div.equals("Either")) {
1130                                log.debug("Aspect {} added as both Diverging and Normal Route", advancedAspect[i]);
1131                                nonDivergAspects.add(i);
1132                                divergAspects.add(i);
1133                                divergFlagsAvailable = true;
1134                                eitherAspects.add(i);
1135                                log.debug("Using Diverging Flag");
1136                            } else {
1137                                log.debug("Aspect {} added as Normal Route", advancedAspect[i]);
1138                                nonDivergAspects.add(i);
1139                                log.debug("Aspect {} added as Normal Route", advancedAspect[i]);
1140                            }
1141                        } else {
1142                            nonDivergAspects.add(i);
1143                            log.debug("Aspect {} added as Normal Route", advancedAspect[i]);
1144                        }
1145                    }
1146                    if ((eitherAspects.equals(divergAspects)) && (divergAspects.size() < nonDivergAspects.size())) {
1147                        //There are no unique diverging aspects
1148                        log.debug("'Either' aspects equals divergAspects and is less than non-diverging aspects");
1149                        divergFlagsAvailable = false;
1150                    }
1151                    log.debug("path max speed : {}", maxPathSpeed);
1152                    for (int i = 0; i < advancedAspect.length; i++) {
1153                        if (!getSourceMast().isAspectDisabled(advancedAspect[i])) {
1154                            String strSpeed = (String) getSourceMast().getSignalSystem().getProperty(advancedAspect[i], "speed");
1155                            log.debug("Aspect Speed = {} for aspect {}", strSpeed, advancedAspect[i]);
1156                            /*  if the diverg flags available is set and the diverg aspect
1157                             array contains the entry then we will check this aspect.
1158
1159                             If the diverg flag has not been set then we will check.
1160                             */
1161                            log.debug("advanced aspect {}",advancedAspect[i]);
1162                            if ((divergRoute && (divergFlagsAvailable) && (divergAspects.contains(i))) || ((divergRoute && !divergFlagsAvailable) || (!divergRoute)) && (nonDivergAspects.contains(i))) {
1163                                log.debug("In list");
1164                                if ((strSpeed != null) && (!strSpeed.isEmpty())) {
1165                                    float speed = 0.0f;
1166                                    try {
1167                                        speed = Float.parseFloat(strSpeed);
1168                                    } catch (NumberFormatException nx) {
1169                                        // not a number, perhaps a name?
1170                                        try {
1171                                            speed = InstanceManager.getDefault(SignalSpeedMap.class).getSpeed(strSpeed);
1172                                        } catch (IllegalArgumentException ex) {
1173                                            // not a name either
1174                                            log.warn("Using speed = 0.0 because could not understand \"{}\"", strSpeed);
1175                                        }
1176                                    }
1177                                    //Integer state = Integer.parseInt(strSpeed);
1178                                    /* This pics out either the highest speed signal if there
1179                                     * is no block speed specified or the highest speed signal
1180                                     * that is under the minimum block speed.
1181                                     */
1182                                    log.debug("{} signal state speed {} maxSigSpeed {} maxPathSpeed {}", destination.getDisplayName(), speed, maxSigSpeed, maxPathSpeed);
1183                                    if (maxPathSpeed == 0) {
1184                                        if (maxSigSpeed == -1) {
1185                                            log.debug("min speed on this route is equal to 0 so will set this as our max speed");
1186                                            maxSigSpeed = speed;
1187                                            aspect = advancedAspect[i];
1188                                            log.debug("Aspect to set is {}", aspect);
1189                                        } else if (speed > maxSigSpeed) {
1190                                            log.debug("new speed is faster than old will use this");
1191                                            maxSigSpeed = speed;
1192                                            aspect = advancedAspect[i];
1193                                            log.debug("Aspect to set is {}", aspect);
1194                                        }
1195                                    } else if ((speed > maxSigSpeed) && (maxSigSpeed < maxPathSpeed) && (speed <= maxPathSpeed)) {
1196                                        //Only set the speed to the lowest if the max speed is greater than the path speed
1197                                        //and the new speed is less than the last max speed
1198                                        log.debug("our minimum speed on this route is less than our state speed, we will set this as our max speed");
1199                                        maxSigSpeed = speed;
1200                                        aspect = advancedAspect[i];
1201                                        log.debug("Aspect to set is {}", aspect);
1202                                    } else if ((maxSigSpeed > maxPathSpeed) && (speed < maxSigSpeed)) {
1203                                        log.debug("our max signal speed is greater than our path speed on this route, our speed is less that the maxSigSpeed");
1204                                        maxSigSpeed = speed;
1205                                        aspect = advancedAspect[i];
1206                                        log.debug("Aspect to set is {}", aspect);
1207
1208                                    } else if (maxSigSpeed == -1) {
1209                                        log.debug("maxSigSpeed returned as -1");
1210                                        maxSigSpeed = speed;
1211                                        aspect = advancedAspect[i];
1212                                        log.debug("Aspect to set is {}", aspect);
1213                                    }
1214                                }
1215                            }
1216                        } else {
1217                            log.debug("Aspect has been disabled {}", advancedAspect[i]);
1218                        }
1219                    }
1220                }
1221            }
1222            if ((aspect != null) && (!aspect.isEmpty())) {
1223                log.debug("setMastAppearance setting aspect \"{}\"", aspect);
1224                String aspectSet = aspect; // for lambda
1225                try {
1226                    ThreadingUtil.runOnLayout(() -> {
1227                        getSourceMast().setAspect(aspectSet);
1228                    });
1229                } catch (Exception ex) {
1230                    log.error("Exception while setting Signal Logic", ex);
1231                }
1232                return;
1233            }
1234        }
1235        log.debug("Aspect returned is not valid, setting stop");
1236        ThreadingUtil.runOnLayout(() -> {
1237            getSourceMast().setAspect(stopAspect);
1238        });
1239    }
1240
1241    /**
1242     * {@inheritDoc }
1243     */
1244    @Override
1245    public void setConflictingLogic(SignalMast sm, LevelXing lx) {
1246        if (sm == null) {
1247            return;
1248        }
1249        log.debug("setConflicting logic mast {}", sm.getDisplayName());
1250        if (sm == source) {
1251            log.debug("source is us so exit");
1252            return;
1253        }
1254        Enumeration<SignalMast> en = destList.keys();
1255        while (en.hasMoreElements()) {
1256            SignalMast dm = en.nextElement();
1257            if (destList.get(dm).isBlockIncluded(lx.getLayoutBlockAC())) {
1258                destList.get(dm).addAutoSignalMast(sm);
1259            } else if (destList.get(dm).isBlockIncluded(lx.getLayoutBlockBD())) {
1260                destList.get(dm).addAutoSignalMast(sm);
1261            } else if (destList.get(dm).isAutoBlockIncluded(lx.getLayoutBlockAC())) {
1262                destList.get(dm).addAutoSignalMast(sm);
1263            } else if (destList.get(dm).isAutoBlockIncluded(lx.getLayoutBlockBD())) {
1264                destList.get(dm).addAutoSignalMast(sm);
1265            } else {
1266                log.debug("Block not found");
1267            }
1268        }
1269    }
1270
1271    /**
1272     * {@inheritDoc }
1273     */
1274    @Override
1275    public void removeConflictingLogic(SignalMast sm, LevelXing lx) {
1276        if (sm == source) {
1277            return;
1278        }
1279        Enumeration<SignalMast> en = destList.keys();
1280        while (en.hasMoreElements()) {
1281            SignalMast dm = en.nextElement();
1282            if (destList.get(dm).isBlockIncluded(lx.getLayoutBlockAC())) {
1283                destList.get(dm).removeAutoSignalMast(sm);
1284            } else if (destList.get(dm).isBlockIncluded(lx.getLayoutBlockBD())) {
1285                destList.get(dm).removeAutoSignalMast(sm);
1286            }
1287        }
1288    }
1289
1290    /**
1291     * Class to store SML properties for a destination mast paired with this
1292     * source mast.
1293     */
1294    private class DestinationMast {
1295
1296        LayoutBlock destinationBlock = null;
1297        LayoutBlock protectingBlock = null; //this is the block that the source signal is protecting
1298
1299        List<NamedBeanSetting> userSetTurnouts = new ArrayList<>(0);
1300        Hashtable<Turnout, Integer> autoTurnouts = new Hashtable<>(0);
1301        //Hashtable<Turnout, Boolean> turnoutThroats = new Hashtable<Turnout, Boolean>(0);
1302        //Hashtable<Turnout, Boolean> autoTurnoutThroats = new Hashtable<Turnout, Boolean>(0);
1303
1304        List<NamedBeanSetting> userSetMasts = new ArrayList<>(0);
1305        Hashtable<SignalMast, String> autoMasts = new Hashtable<>(0);
1306        List<NamedBeanSetting> userSetSensors = new ArrayList<>(0);
1307        List<NamedBeanSetting> userSetBlocks = new ArrayList<>(0);
1308        boolean turnoutThrown = false;
1309        boolean permissiveBlock = false;
1310        boolean disposed = false;
1311
1312        List<LevelXing> blockInXings = new ArrayList<>();
1313
1314        //autoBlocks are for those automatically generated by the system.
1315        LinkedHashMap<Block, Integer> autoBlocks = new LinkedHashMap<>(0);
1316
1317        List<Block> xingAutoBlocks = new ArrayList<>(0);
1318        List<Block> dblCrossoverAutoBlocks = new ArrayList<>(0);
1319        SignalMast destinationSignalMast;
1320        boolean active = false;
1321        boolean destMastInit = false;
1322
1323        float minimumBlockSpeed = 0.0f;
1324
1325        boolean useLayoutEditor = false;
1326        boolean useLayoutEditorTurnouts = false;
1327        boolean useLayoutEditorBlocks = false;
1328        boolean lockTurnouts = false;
1329
1330        NamedBeanHandle<Section> associatedSection = null;
1331
1332        DestinationMast(SignalMast destination) {
1333            this.destinationSignalMast = destination;
1334            if (destination.getAspect() == null) {
1335                try {
1336                    destination.setAspect(destination.getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.DANGER));
1337                } catch (Exception ex) {
1338                    log.error("Error while creating Signal Logic", ex);
1339                }
1340            }
1341        }
1342
1343        void updateDestinationMast(SignalMast newMast) {
1344            destinationSignalMast = newMast;
1345            if (destinationSignalMast.getAspect() == null) {
1346                try {
1347                    destinationSignalMast.setAspect(destinationSignalMast.getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.DANGER));
1348                } catch (Exception ex) {
1349                    log.error("Error while creating Signal Logic", ex);
1350                }
1351            }
1352        }
1353
1354        LayoutBlock getProtectingBlock() {
1355            return protectingBlock;
1356        }
1357
1358        String getDisplayName() {
1359            return destinationSignalMast.getDisplayName();
1360        }
1361
1362        String comment;
1363
1364        String getComment() {
1365            return comment;
1366        }
1367
1368        void setComment(String comment) {
1369            String old = this.comment;
1370            this.comment = comment;
1371            firePropertyChange(PROPERTY_COMMENT, old, comment);
1372        }
1373
1374        boolean isActive() {
1375            if (disposed) {
1376                log.error("checkState called even though this has been disposed of");
1377                return false;
1378            }
1379            return active;
1380        }
1381
1382        float getMinimumSpeed() {
1383            return minimumBlockSpeed;
1384        }
1385
1386        boolean enable = true;
1387
1388        void setEnabled() {
1389            enable = true;
1390            firePropertyChange(PROPERTY_ENABLED, false, this.destinationSignalMast);
1391        }
1392
1393        void setDisabled() {
1394            enable = false;
1395            firePropertyChange(PROPERTY_ENABLED, true, this.destinationSignalMast);
1396        }
1397
1398        boolean isEnabled() {
1399            return enable;
1400        }
1401
1402        int store = STOREALL;
1403
1404        void setStore(int store) {
1405            this.store = store;
1406        }
1407
1408        int getStoreState() {
1409            return store;
1410        }
1411
1412        void setAssociatedSection(Section section) {
1413            if (section != null && (!useLayoutEditor || !useLayoutEditorBlocks)) {
1414                log.warn("This Logic {} to {} is not using the Layout Editor or its Blocks, the associated Section will not be populated correctly", source.getDisplayName(), destinationSignalMast.getDisplayName());
1415            }
1416            if (section == null) {
1417                associatedSection = null;
1418                return;
1419            }
1420            associatedSection = InstanceManager.getDefault(NamedBeanHandleManager.class).getNamedBeanHandle(section.getDisplayName(), section);
1421            if (!autoBlocks.isEmpty()) { // associatedSection is guaranteed to exist
1422                createSectionDetails();
1423            }
1424        }
1425
1426        Section getAssociatedSection() {
1427            if (associatedSection != null) {
1428                return associatedSection.getBean();
1429            }
1430            return null;
1431        }
1432
1433        void createSectionDetails() {
1434            getAssociatedSection().removeAllBlocksFromSection();
1435            getAutoBlocksBetweenMasts().forEach(key -> {
1436                getAssociatedSection().addBlock(key);
1437            });
1438            String dir = Path.decodeDirection(getFacingBlock().getNeighbourDirection(getProtectingBlock()));
1439            EntryPoint ep = new EntryPoint(getProtectingBlock().getBlock(), getFacingBlock().getBlock(), dir);
1440            ep.setTypeForward();
1441            getAssociatedSection().addToForwardList(ep);
1442
1443            LayoutBlock proDestLBlock = InstanceManager.getDefault(LayoutBlockManager.class).getProtectedBlockByNamedBean(destinationSignalMast, destinationBlock.getMaxConnectedPanel());
1444            if (proDestLBlock != null) {
1445                log.debug("Add protecting Block {}", proDestLBlock.getDisplayName());
1446                dir = Path.decodeDirection(proDestLBlock.getNeighbourDirection(destinationBlock));
1447                ep = new EntryPoint(destinationBlock.getBlock(), proDestLBlock.getBlock(), dir);
1448                ep.setTypeReverse();
1449                getAssociatedSection().addToReverseList(ep);
1450            } else {
1451                log.debug(" ### Protecting Block not found ### ");
1452            }
1453        }
1454
1455        boolean isTurnoutLockAllowed() {
1456            return lockTurnouts;
1457        }
1458
1459        void allowTurnoutLock(boolean lock) {
1460            if (lockTurnouts == lock) {
1461                return;
1462            }
1463            if (!lock) {
1464                clearTurnoutLock();
1465            }
1466            lockTurnouts = lock;
1467        }
1468
1469        void setTurnouts(Hashtable<NamedBeanHandle<Turnout>, Integer> turnouts) {
1470            if (this.userSetTurnouts != null) {
1471                userSetTurnouts.forEach(nbh ->
1472                    nbh.getBean().removePropertyChangeListener(propertyTurnoutListener));
1473            }
1474            destMastInit = false;
1475            if (turnouts == null) {
1476                userSetTurnouts = new ArrayList<>(0);
1477            } else {
1478                userSetTurnouts = new ArrayList<>();
1479                Enumeration<NamedBeanHandle<Turnout>> e = turnouts.keys();
1480                while (e.hasMoreElements()) {
1481                    NamedBeanHandle<Turnout> nbh = e.nextElement();
1482                    NamedBeanSetting nbs = new NamedBeanSetting(nbh, turnouts.get(nbh));
1483                    userSetTurnouts.add(nbs);
1484                }
1485            }
1486            firePropertyChange(PROPERTY_TURNOUTS, null, this.destinationSignalMast);
1487        }
1488
1489        void setAutoTurnouts(Hashtable<Turnout, Integer> turnouts) {
1490            log.debug("{} called setAutoTurnouts with {}", destinationSignalMast.getDisplayName(),
1491                (turnouts != null ? "" + turnouts.size() + " turnouts in hash table" : "null hash table reference"));
1492            if (this.autoTurnouts != null) {
1493                Enumeration<Turnout> keys = this.autoTurnouts.keys();
1494                while (keys.hasMoreElements()) {
1495                    Turnout key = keys.nextElement();
1496                    key.removePropertyChangeListener(propertyTurnoutListener);
1497                }
1498                //minimumBlockSpeed = 0;
1499            }
1500            destMastInit = false;
1501            if (turnouts == null) {
1502                this.autoTurnouts = new Hashtable<>(0);
1503            } else {
1504                this.autoTurnouts = new Hashtable<>(turnouts);
1505            }
1506            firePropertyChange(PROPERTY_AUTO_TURNOUTS, null, this.destinationSignalMast);
1507        }
1508
1509        void setBlocks(Hashtable<Block, Integer> blocks) {
1510            log.debug("{} Set blocks called", destinationSignalMast.getDisplayName());
1511            if (this.userSetBlocks != null) {
1512                userSetBlocks.forEach( nbh ->
1513                    nbh.getBean().removePropertyChangeListener(propertyBlockListener));
1514            }
1515            destMastInit = false;
1516
1517            userSetBlocks = new ArrayList<>(0);
1518            if (blocks != null) {
1519                userSetBlocks = new ArrayList<>();
1520                Enumeration<Block> e = blocks.keys();
1521                while (e.hasMoreElements()) {
1522                    Block blk = e.nextElement();
1523                    NamedBeanHandle<?> nbh = InstanceManager.getDefault(NamedBeanHandleManager.class).getNamedBeanHandle(blk.getDisplayName(), blk);
1524                    NamedBeanSetting nbs = new NamedBeanSetting(nbh, blocks.get(blk));
1525                    userSetBlocks.add(nbs);
1526                }
1527            }
1528            firePropertyChange(PROPERTY_BLOCKS, null, this.destinationSignalMast);
1529        }
1530
1531        public void setAutoBlocks(LinkedHashMap<Block, Integer> blocks) {
1532            if (log.isDebugEnabled()) {
1533                log.debug("{} called setAutoBlocks with {}", destinationSignalMast.getDisplayName(),
1534                    (blocks != null ? "" + blocks.size() + " blocks in hash table" : "null hash table reference"));
1535            }
1536            if (this.autoBlocks != null) {
1537                autoBlocks.keySet().forEach( key ->
1538                    key.removePropertyChangeListener(propertyBlockListener));
1539            }
1540            destMastInit = false;
1541            if (blocks == null) {
1542                this.autoBlocks = new LinkedHashMap<>(0);
1543
1544            } else {
1545                this.autoBlocks = new LinkedHashMap<>(blocks);
1546                //We shall remove the facing block in the list.
1547                if (facingBlock != null && autoBlocks.containsKey(facingBlock.getBlock())) {
1548                    autoBlocks.remove(facingBlock.getBlock());
1549                }
1550                if (getAssociatedSection() != null) {
1551                    createSectionDetails();
1552                }
1553            }
1554            firePropertyChange(PROPERTY_AUTO_BLOCKS, null, this.destinationSignalMast);
1555        }
1556
1557        void setMasts(Hashtable<SignalMast, String> masts) {
1558            if (this.userSetMasts != null) {
1559                userSetMasts.forEach( nbh ->
1560                    nbh.getBean().removePropertyChangeListener(propertySignalMastListener));
1561            }
1562
1563            destMastInit = false;
1564
1565            if (masts == null) {
1566                userSetMasts = new ArrayList<>(0);
1567            } else {
1568                userSetMasts = new ArrayList<>();
1569                Enumeration<SignalMast> e = masts.keys();
1570                while (e.hasMoreElements()) {
1571                    SignalMast mast = e.nextElement();
1572                    NamedBeanHandle<?> nbh = InstanceManager.getDefault(NamedBeanHandleManager.class)
1573                        .getNamedBeanHandle(mast.getDisplayName(), mast);
1574                    NamedBeanSetting nbs = new NamedBeanSetting(nbh, masts.get(mast));
1575                    userSetMasts.add(nbs);
1576                }
1577            }
1578            firePropertyChange(PROPERTY_MASTS, null, this.destinationSignalMast);
1579        }
1580
1581        /**
1582         *
1583         * @param newAutoMasts Hashtable of signal masts and set to Aspects
1584         * @param overwrite    When true, replace an existing autoMasts list in
1585         *                     the SML
1586         */
1587        void setAutoMasts(Hashtable<SignalMast, String> newAutoMasts, boolean overwrite) {
1588            log.debug("{} setAutoMast Called", destinationSignalMast.getDisplayName());
1589            if (this.autoMasts != null) {
1590                Enumeration<SignalMast> keys = this.autoMasts.keys();
1591                while (keys.hasMoreElements()) {
1592                    SignalMast key = keys.nextElement();
1593                    key.removePropertyChangeListener(propertySignalMastListener);
1594                }
1595                //minimumBlockSpeed = 0;
1596            }
1597            destMastInit = false;
1598            if (overwrite) {
1599                if (newAutoMasts == null) {
1600                    this.autoMasts = new Hashtable<>(0);
1601                } else {
1602                    this.autoMasts = new Hashtable<>(newAutoMasts);
1603                }
1604            } else {
1605                if (newAutoMasts == null) {
1606                    this.autoMasts = new Hashtable<>(0);
1607                } else {
1608                    Enumeration<SignalMast> keys = newAutoMasts.keys();
1609                    while (keys.hasMoreElements()) {
1610                        SignalMast key = keys.nextElement();
1611                        this.autoMasts.put(key, newAutoMasts.get(key));
1612                    }
1613                }
1614            }
1615            //kick off the process to add back in signal masts at crossings.
1616            for (int i = 0; i < blockInXings.size(); i++) {
1617                blockInXings.get(i).addSignalMastLogic(source);
1618            }
1619
1620            firePropertyChange(PROPERTY_AUTO_MASTS, null, this.destinationSignalMast);
1621        }
1622
1623        void setSensors(Hashtable<NamedBeanHandle<Sensor>, Integer> sensors) {
1624            if (this.userSetSensors != null) {
1625                userSetSensors.forEach(nbh ->
1626                    nbh.getBean().removePropertyChangeListener(propertySensorListener));
1627            }
1628            destMastInit = false;
1629
1630            if (sensors == null) {
1631                userSetSensors = new ArrayList<>(0);
1632            } else {
1633                userSetSensors = new ArrayList<>();
1634                Enumeration<NamedBeanHandle<Sensor>> e = sensors.keys();
1635                while (e.hasMoreElements()) {
1636                    NamedBeanHandle<Sensor> nbh = e.nextElement();
1637                    NamedBeanSetting nbs = new NamedBeanSetting(nbh, sensors.get(nbh));
1638                    userSetSensors.add(nbs);
1639                }
1640            }
1641            firePropertyChange(PROPERTY_SENSORS, null, this.destinationSignalMast);
1642        }
1643
1644        void addSensor(NamedBeanHandle<Sensor> sen, int state) {
1645            for (NamedBeanSetting nbh : userSetSensors) {
1646                if (nbh.getBean().equals(sen.getBean())) {
1647                    return;
1648                }
1649            }
1650            sen.getBean().addPropertyChangeListener(propertySensorListener);
1651            NamedBeanSetting nbs = new NamedBeanSetting(sen, state);
1652            userSetSensors.add(nbs);
1653            firePropertyChange(PROPERTY_SENSORS, null, this.destinationSignalMast);
1654        }
1655
1656// not used now, preserved for later use
1657//         void removeSensor(NamedBeanHandle<Sensor> sen) {
1658//             for (NamedBeanSetting nbh : userSetSensors) {
1659//                 if (nbh.getBean().equals(sen.getBean())) {
1660//                     sen.getBean().removePropertyChangeListener(propertySensorListener);
1661//                     userSetSensors.remove(nbh);
1662//                     firePropertyChange("sensors", null, this.destination);
1663//                     return;
1664//                 }
1665//             }
1666//         }
1667
1668        void removeSensor(Sensor sen) {
1669            for (NamedBeanSetting nbh : userSetSensors) {
1670                if (nbh.getBean().equals(sen)) {
1671                    sen.removePropertyChangeListener(propertySensorListener);
1672                    userSetSensors.remove(nbh);
1673                    firePropertyChange(PROPERTY_SENSORS, null, this.destinationSignalMast);
1674                    return;
1675                }
1676            }
1677        }
1678
1679        List<Block> getBlocks() {
1680            List<Block> out = new ArrayList<>();
1681            userSetBlocks.forEach( nbh -> out.add((Block) nbh.getBean()));
1682            return out;
1683        }
1684
1685        List<Block> getAutoBlocks() {
1686            List<Block> out = new ArrayList<>();
1687            Set<Block> blockKeys = autoBlocks.keySet();
1688            blockKeys.forEach(key -> out.add(key));
1689            return out;
1690        }
1691
1692        List<Block> getAutoBlocksBetweenMasts() {
1693            if (destList.get(destinationSignalMast).xingAutoBlocks.isEmpty()
1694                    && destList.get(destinationSignalMast).dblCrossoverAutoBlocks.isEmpty()) {
1695                return getAutoBlocks();
1696            }
1697            List<Block> returnList = getAutoBlocks();
1698            for (Block blk : getAutoBlocks()) {
1699                if (xingAutoBlocks.contains(blk)) {
1700                    returnList.remove(blk);
1701                }
1702                if (dblCrossoverAutoBlocks.contains(blk)) {
1703                    returnList.remove(blk);
1704                }
1705            }
1706
1707            return returnList;
1708        }
1709
1710        List<Turnout> getTurnouts() {
1711            List<Turnout> out = new ArrayList<>();
1712            userSetTurnouts.forEach( nbh -> out.add((Turnout) nbh.getBean()));
1713            return out;
1714        }
1715
1716        void removeTurnout(Turnout turn) {
1717            Iterator<NamedBeanSetting> nbh = userSetTurnouts.iterator();
1718            while (nbh.hasNext()) {
1719                NamedBeanSetting i = nbh.next();
1720                if (i.getBean().equals(turn)) {
1721                    turn.removePropertyChangeListener(propertyTurnoutListener);
1722                    nbh.remove();
1723                    firePropertyChange(PROPERTY_TURNOUTS, null, this.destinationSignalMast);
1724                }
1725            }
1726        }
1727
1728        @SuppressWarnings("unchecked") // (NamedBeanHandle<Turnout>) nbh.getNamedBean() is unchecked cast
1729        List<NamedBeanHandle<Turnout>> getNamedTurnouts() {
1730            List<NamedBeanHandle<Turnout>> out = new ArrayList<>();
1731            userSetTurnouts.forEach(nbh ->
1732                out.add((NamedBeanHandle<Turnout>) nbh.getNamedBean()));
1733            return out;
1734        }
1735
1736        List<Turnout> getAutoTurnouts() {
1737            List<Turnout> out = new ArrayList<>();
1738            Enumeration<Turnout> en = autoTurnouts.keys();
1739            while (en.hasMoreElements()) {
1740                out.add(en.nextElement());
1741            }
1742            return out;
1743        }
1744
1745        List<SignalMast> getSignalMasts() {
1746            List<SignalMast> out = new ArrayList<>();
1747            userSetMasts.forEach( nbh -> out.add((SignalMast) nbh.getBean()));
1748            return out;
1749        }
1750
1751        List<SignalMast> getAutoSignalMasts() {
1752            List<SignalMast> out = new ArrayList<>();
1753            Enumeration<SignalMast> en = autoMasts.keys();
1754            while (en.hasMoreElements()) {
1755                out.add(en.nextElement());
1756            }
1757            return out;
1758        }
1759
1760        List<Sensor> getSensors() {
1761            List<Sensor> out = new ArrayList<>();
1762            userSetSensors.forEach( nbh -> out.add((Sensor) nbh.getBean()));
1763            return out;
1764        }
1765
1766        @SuppressWarnings("unchecked") // (NamedBeanHandle<Sensor>) nbh.getNamedBean() is unchecked cast
1767        List<NamedBeanHandle<Sensor>> getNamedSensors() {
1768            List<NamedBeanHandle<Sensor>> out = new ArrayList<>();
1769            userSetSensors.forEach( nbh -> out.add((NamedBeanHandle<Sensor>) nbh.getNamedBean()));
1770            return out;
1771        }
1772
1773        boolean isBlockIncluded(Block block) {
1774            return userSetBlocks.stream().anyMatch(nbh -> (nbh.getBean().equals(block)));
1775        }
1776
1777        boolean isAutoBlockIncluded(LayoutBlock block) {
1778            if (block != null) {
1779                return autoBlocks.containsKey(block.getBlock());
1780            }
1781            return false;
1782        }
1783
1784        boolean isAutoBlockIncluded(Block block) {
1785            return autoBlocks.containsKey(block);
1786        }
1787
1788        boolean isBlockIncluded(LayoutBlock block) {
1789            return userSetBlocks.stream().anyMatch(nbh -> (nbh.getBean().equals(block.getBlock())));
1790        }
1791
1792        boolean isTurnoutIncluded(Turnout turnout) {
1793            return userSetTurnouts.stream().anyMatch(nbh -> (nbh.getBean().equals(turnout)));
1794        }
1795
1796        boolean isSensorIncluded(Sensor sensor) {
1797            return userSetSensors.stream().anyMatch(nbh -> (nbh.getBean().equals(sensor)));
1798        }
1799
1800        boolean isSignalMastIncluded(SignalMast signal) {
1801            return userSetMasts.stream().anyMatch(nbh -> (nbh.getBean().equals(signal)));
1802        }
1803
1804        int getAutoBlockState(Block block) {
1805            if (autoBlocks == null) {
1806                return -1;
1807            }
1808            return autoBlocks.get(block);
1809        }
1810
1811        int getBlockState(Block block) {
1812            if (userSetBlocks == null) {
1813                return -1;
1814            }
1815            for (NamedBeanSetting nbh : userSetBlocks) {
1816                if (nbh.getBean().equals(block)) {
1817                    return nbh.getSetting();
1818                }
1819            }
1820            return -1;
1821        }
1822
1823        int getSensorState(Sensor sensor) {
1824            if (userSetSensors == null) {
1825                return -1;
1826            }
1827            for (NamedBeanSetting nbh : userSetSensors) {
1828                if (nbh.getBean().equals(sensor)) {
1829                    return nbh.getSetting();
1830                }
1831            }
1832            return -1;
1833        }
1834
1835        int getTurnoutState(Turnout turnout) {
1836            if (userSetTurnouts == null) {
1837                return -1;
1838            }
1839            for (NamedBeanSetting nbh : userSetTurnouts) {
1840                if (nbh.getBean().equals(turnout)) {
1841                    return nbh.getSetting();
1842                }
1843            }
1844            return -1;
1845        }
1846
1847        int getAutoTurnoutState(Turnout turnout) {
1848            if (autoTurnouts == null) {
1849                return -1;
1850            }
1851            if (autoTurnouts.containsKey(turnout)) {
1852                return autoTurnouts.get(turnout);
1853            }
1854            return -1;
1855        }
1856
1857        String getSignalMastState(SignalMast mast) {
1858            if (userSetMasts == null) {
1859                return null;
1860            }
1861            for (NamedBeanSetting nbh : userSetMasts) {
1862                if (nbh.getBean().equals(mast)) {
1863                    return nbh.getStringSetting();
1864                }
1865            }
1866            return null;
1867        }
1868
1869        String getAutoSignalMastState(SignalMast mast) {
1870            if (autoMasts == null) {
1871                return null;
1872            }
1873            return autoMasts.get(mast);
1874        }
1875
1876        // the following 2 methods are not supplied in the implementation
1877        boolean inWait = false;
1878
1879        /*
1880         * Before going active or checking that we can go active, wait
1881         * for things to settle down to help prevent a race condition.
1882         */
1883        void checkState() {
1884            if (disposed) {
1885                log.error("checkState called even though this has been disposed of {}",
1886                    getSourceMast().getDisplayName());
1887                return;
1888            }
1889
1890            if (!enable) {
1891                return;
1892            }
1893            if (inWait) {
1894                return;
1895            }
1896
1897            log.debug("check Signal Dest State called");
1898            inWait = true;
1899
1900            // The next line forces a single initialization of InstanceManager.getDefault(SignalMastLogicManager.class)
1901            // before launching parallel threads
1902            int tempDelay = InstanceManager.getDefault(SignalMastLogicManager.class).getSignalLogicDelay();
1903
1904            ThreadingUtil.runOnLayoutDelayed(
1905                    () -> {
1906                        checkStateDetails();
1907                        inWait = false;
1908                    }, tempDelay
1909            );
1910        }
1911
1912        /**
1913         * Check the details of this source-destination signal mast logic pair.
1914         * Steps through every sensor, turnout etc. before setting the SML
1915         * Aspect on the source mast via {
1916         *
1917         * @see #setSignalAppearance } and {
1918         * @see #setMastAppearance }
1919         */
1920        private void checkStateDetails() {
1921            turnoutThrown = false;
1922            permissiveBlock = false;
1923            if (disposed) {
1924                log.error("checkStateDetails called even though this has been disposed of {} {}",
1925                    getSourceMast().getDisplayName(), destinationSignalMast.getDisplayName());
1926                return;
1927            }
1928            if (!enable) {
1929                return;
1930            }
1931            log.debug("From {} to {} internal check state", getSourceMast().getDisplayName(),
1932                destinationSignalMast.getDisplayName());
1933            active = false;
1934            if ((useLayoutEditor) && (autoTurnouts.isEmpty()) && (autoBlocks.isEmpty())) {
1935                return;
1936            }
1937            boolean state = true;
1938            Enumeration<Turnout> keys = autoTurnouts.keys();
1939            while (keys.hasMoreElements()) {
1940                Turnout key = keys.nextElement();
1941                if (key.getKnownState() != autoTurnouts.get(key)) {
1942                    if (key.getState() != autoTurnouts.get(key)) {
1943                        if (isTurnoutIncluded(key)) {
1944                            if (key.getState() != getTurnoutState(key)) {
1945                                state = false;
1946                            } else if (key.getState() == Turnout.THROWN) {
1947                                turnoutThrown = true;
1948                            }
1949                        } else {
1950                            state = false;
1951                        }
1952                    }
1953                } else if (key.getState() == Turnout.THROWN) {
1954                    turnoutThrown = true;
1955                }
1956            }
1957
1958            for (NamedBeanSetting nbh : userSetTurnouts) {
1959                Turnout key = (Turnout) nbh.getBean();
1960                if (key.getKnownState() != nbh.getSetting()) {
1961                    state = false;
1962                } else if (key.getState() == Turnout.THROWN) {
1963                    turnoutThrown = true;
1964                }
1965            }
1966
1967            Enumeration<SignalMast> mastKeys = autoMasts.keys();
1968            while (mastKeys.hasMoreElements()) {
1969                SignalMast key = mastKeys.nextElement();
1970                String aspect = key.getAspect();
1971                log.debug("key {} {} {}", key.getDisplayName(), aspect, autoMasts.get(key));
1972                if ((aspect != null) && (!aspect.equals(autoMasts.get(key)))) {
1973                    if (isSignalMastIncluded(key)) {
1974                        //Basically if we have a blank aspect, we don't care about the state of the signalmast
1975                        if (!getSignalMastState(key).isEmpty()) {
1976                            if (!aspect.equals(getSignalMastState(key))) {
1977                                state = false;
1978                            }
1979                        }
1980                    } else {
1981                        state = false;
1982                    }
1983                }
1984            }
1985            for (NamedBeanSetting nbh : userSetMasts) {
1986                SignalMast key = (SignalMast) nbh.getBean();
1987                String aspect = key.getAspect();
1988                if ((aspect == null) || (!aspect.equals(nbh.getStringSetting()))) {
1989                    state = false;
1990                }
1991            }
1992
1993            for (NamedBeanSetting nbh : userSetSensors) {
1994                Sensor key = (Sensor) nbh.getBean();
1995                if (key.getKnownState() != nbh.getSetting()) {
1996                    state = false;
1997                }
1998            }
1999
2000            for (Map.Entry<Block, Integer> entry : this.autoBlocks.entrySet()) {
2001                log.debug(" entry {} {} {}", entry.getKey().getDisplayName(),
2002                    entry.getKey().getState(), entry.getValue());
2003                if (entry.getKey().getState() != autoBlocks.get(entry.getKey())) {
2004                    if (isBlockIncluded(entry.getKey())) {
2005                        if (getBlockState(entry.getKey()) != 0x03) {
2006                            if (entry.getKey().getState() != getBlockState(entry.getKey())) {
2007                                if (entry.getKey().getState() == Block.OCCUPIED && entry.getKey().getPermissiveWorking()) {
2008                                    permissiveBlock = true;
2009                                } else {
2010                                    state = false;
2011                                }
2012                            }
2013                        }
2014                    } else {
2015                        if (entry.getKey().getState() == Block.OCCUPIED && entry.getKey().getPermissiveWorking()) {
2016                            permissiveBlock = true;
2017                        } else if (entry.getKey().getState() == Block.UNDETECTED) {
2018                            log.debug("Block {} is UNDETECTED so treat as unoccupied", entry.getKey().getDisplayName());
2019                        } else {
2020                            state = false;
2021                        }
2022                    }
2023                }
2024            }
2025
2026            for (NamedBeanSetting nbh : userSetBlocks) {
2027                Block key = (Block) nbh.getBean();
2028                if (nbh.getSetting() != 0x03) {
2029                    if (key.getState() != nbh.getSetting()) {
2030                        if (key.getState() == Block.OCCUPIED && key.getPermissiveWorking()) {
2031                            permissiveBlock = true;
2032                        } else {
2033                            state = false;
2034                        }
2035                    }
2036                }
2037            }
2038            if (permissiveBlock
2039                /*If a block has been found to be permissive, but the source signalmast
2040                 does not support a call-on/permissive aspect then the route can not be set*/
2041                && getSourceMast().getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.PERMISSIVE) == null) {
2042                    state = false;
2043            }
2044
2045            /*This check is purely for use with the dispatcher, it will check to see if any of the blocks are set to "useExtraColor"
2046             which is a means to determine if the block is in a section that is occupied and it not ours thus we can set the signal to danger.*/
2047            if (state && getAssociatedSection() != null
2048                    && InstanceManager.getNullableDefault(jmri.jmrit.dispatcher.DispatcherFrame.class) != null
2049                    && InstanceManager.getNullableDefault(LayoutBlockManager.class) != null
2050                    && getAssociatedSection().getState() != Section.FORWARD) {
2051
2052                LayoutBlockManager lbm = InstanceManager.getDefault(LayoutBlockManager.class);
2053                for (Block key : autoBlocks.keySet()) {
2054                    LayoutBlock lb = lbm.getLayoutBlock(key);
2055                    if (lb != null && lb.getUseExtraColor()) {
2056                        state = false;
2057                        break;
2058                    }
2059                }
2060                if (!state) {
2061                    for (NamedBeanSetting nbh : userSetBlocks) {
2062                        Block key = (Block) nbh.getBean();
2063                        LayoutBlock lb = lbm.getLayoutBlock(key);
2064                        if (lb != null && lb.getUseExtraColor()) {
2065                            state = false;
2066                            break;
2067                        }
2068                    }
2069                }
2070            }
2071
2072            if (!state) {
2073                turnoutThrown = false;
2074                permissiveBlock = false;
2075            }
2076
2077            active = state;
2078            ThreadingUtil.runOnLayout(() -> {
2079                setSignalAppearance();
2080            });
2081        }
2082
2083        /**
2084         * Set up this source-destination signal mast logic pair. Steps through
2085         * every list defined on the source mast.
2086         */
2087        void initialise() {
2088            if ((destMastInit) || (disposed)) {
2089                return;
2090            }
2091
2092            active = false;
2093            turnoutThrown = false;
2094            permissiveBlock = false;
2095            boolean routeclear = true;
2096            if ((useLayoutEditor) && (autoTurnouts.isEmpty()) && (autoBlocks.isEmpty()) && (autoMasts.isEmpty())) {
2097                return;
2098            }
2099
2100            calculateSpeed();
2101
2102            Enumeration<Turnout> keys = autoTurnouts.keys();
2103            while (keys.hasMoreElements()) {
2104                Turnout key = keys.nextElement();
2105                key.addPropertyChangeListener(propertyTurnoutListener);
2106
2107                if (key.getKnownState() != autoTurnouts.get(key)) {
2108                    if (key.getState() != autoTurnouts.get(key)) {
2109                        if (isTurnoutIncluded(key)) {
2110                            if (key.getState() != getTurnoutState(key)) {
2111                                routeclear = false;
2112                            } else if (key.getState() == Turnout.THROWN) {
2113                                turnoutThrown = true;
2114                            }
2115                        } else {
2116                            routeclear = false;
2117                        }
2118                    }
2119                } else if (key.getState() == Turnout.THROWN) {
2120                    turnoutThrown = true;
2121                }
2122            }
2123
2124            for (NamedBeanSetting nbh : userSetTurnouts) {
2125                Turnout key = (Turnout) nbh.getBean();
2126                key.addPropertyChangeListener(propertyTurnoutListener, nbh.getBeanName(),
2127                    "Signal Mast Logic:" + source.getDisplayName() + " to " + destinationSignalMast.getDisplayName());
2128                if (key.getKnownState() != nbh.getSetting()) {
2129                    routeclear = false;
2130                } else if (key.getState() == Turnout.THROWN) {
2131                    turnoutThrown = true;
2132                }
2133            }
2134
2135            Enumeration<SignalMast> mastKeys = autoMasts.keys();
2136            while (mastKeys.hasMoreElements()) {
2137                SignalMast key = mastKeys.nextElement();
2138                log.debug("{} auto mast add list {}", destinationSignalMast.getDisplayName(), key.getDisplayName());
2139                key.addPropertyChangeListener(propertySignalMastListener);
2140                String aspect = key.getAspect();
2141                if ( aspect != null && !aspect.equals(autoMasts.get(key))) {
2142                    if (isSignalMastIncluded(key)) {
2143                        if (aspect.equals(getSignalMastState(key))) {
2144                            routeclear = false;
2145                        }
2146                    } else {
2147                        routeclear = false;
2148                    }
2149                }
2150            }
2151
2152            for (NamedBeanSetting nbh : userSetMasts) {
2153                SignalMast key = (SignalMast) nbh.getBean();
2154                key.addPropertyChangeListener(propertySignalMastListener);
2155                String aspect = key.getAspect();
2156                log.debug("mast '{}' key aspect '{}'", destinationSignalMast.getDisplayName(), aspect);
2157                if ((aspect == null) || (!aspect.equals(nbh.getStringSetting()))) {
2158                    routeclear = false;
2159                }
2160            }
2161            for (NamedBeanSetting nbh : userSetSensors) {
2162                Sensor sensor = (Sensor) nbh.getBean();
2163                sensor.addPropertyChangeListener(propertySensorListener, nbh.getBeanName(),
2164                    "Signal Mast Logic:" + source.getDisplayName() + " to " + destinationSignalMast.getDisplayName());
2165                if (sensor.getKnownState() != nbh.getSetting()) {
2166                    routeclear = false;
2167                }
2168            }
2169
2170            for (Map.Entry<Block, Integer> entry : this.autoBlocks.entrySet()) {
2171                log.debug("{} auto block add list {}", destinationSignalMast.getDisplayName(),
2172                    entry.getKey().getDisplayName());
2173                entry.getKey().addPropertyChangeListener(propertyBlockListener);
2174                if (entry.getKey().getState() != entry.getValue()) {
2175                    if (isBlockIncluded(entry.getKey())) {
2176                        if (entry.getKey().getState() != getBlockState(entry.getKey())) {
2177                            if (entry.getKey().getState() == Block.OCCUPIED && entry.getKey().getPermissiveWorking()) {
2178                                permissiveBlock = true;
2179                            } else {
2180                                routeclear = false;
2181                            }
2182                        }
2183                    } else {
2184                        if (entry.getKey().getState() == Block.OCCUPIED && entry.getKey().getPermissiveWorking()) {
2185                            permissiveBlock = true;
2186                        } else if (entry.getKey().getState() == Block.UNDETECTED) {
2187                            log.debug("Block {} is UNDETECTED so treat as unoccupied", entry.getKey().getDisplayName());
2188                        } else {
2189                            routeclear = false;
2190                        }
2191                    }
2192                }
2193            }
2194
2195            for (NamedBeanSetting nbh : userSetBlocks) {
2196                Block key = (Block) nbh.getBean();
2197                key.addPropertyChangeListener(propertyBlockListener);
2198                if (key.getState() != getBlockState(key)) {
2199                    if (key.getState() == Block.OCCUPIED && key.getPermissiveWorking()) {
2200                        permissiveBlock = true;
2201                    } else {
2202                        routeclear = false;
2203                    }
2204                }
2205            }
2206            if ( permissiveBlock
2207                /* If a block has been found to be permissive, but the source signalmast
2208                 does not support a call-on/permissive aspect then the route can not be set */
2209                && getSourceMast().getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.PERMISSIVE) == null) {
2210                    routeclear = false;
2211            }
2212            if (routeclear) {
2213                active = true;
2214                setSignalAppearance();
2215            } else {
2216                permissiveBlock = false;
2217                turnoutThrown = false;
2218            }
2219            destMastInit = true;
2220        }
2221
2222        void useLayoutEditor(boolean boo) throws JmriException {
2223            log.debug("{} called useLayoutEditor({}), is {}",
2224                destinationSignalMast.getDisplayName(), boo, useLayoutEditor);
2225            if (useLayoutEditor == boo) {
2226                return;
2227            }
2228            useLayoutEditor = boo;
2229            if ((boo) && (InstanceManager.getDefault(LayoutBlockManager.class).routingStablised())) {
2230                // JmriException considered normal if there is no valid path using the layout editor.
2231                setupLayoutEditorDetails();
2232            } else {
2233                destinationBlock = null;
2234                facingBlock = null;
2235                protectingBlock = null;
2236                setAutoBlocks(null);
2237                setAutoTurnouts(null);
2238            }
2239        }
2240
2241        void useLayoutEditorDetails(boolean turnouts, boolean blocks) throws JmriException {
2242            log.debug("{} use layout editor details called {}",
2243                destinationSignalMast.getDisplayName(), useLayoutEditor);
2244            useLayoutEditorTurnouts = turnouts;
2245            useLayoutEditorBlocks = blocks;
2246            if ((useLayoutEditor) && (InstanceManager.getDefault(LayoutBlockManager.class).routingStablised())) {
2247                // JmriException considered normal if there is no valid path using the Layout Editor.
2248                setupLayoutEditorDetails();
2249            }
2250        }
2251
2252        void setupLayoutEditorDetails() throws JmriException {
2253            log.debug("setupLayoutEditorDetails: useLayoutEditor={} disposed={}", useLayoutEditor, disposed);
2254            if ((!useLayoutEditor) || (disposed)) {
2255                return;
2256            }
2257            LayoutBlockManager lbm = InstanceManager.getDefault(LayoutBlockManager.class);
2258            if ( destinationBlock != null) {
2259                log.debug("{} Set use layout editor", destinationSignalMast.getDisplayName());
2260            }
2261            Set<LayoutEditor> layout = InstanceManager.getDefault(EditorManager.class).getAll(LayoutEditor.class);
2262            List<LayoutBlock> protectingBlocks = new ArrayList<>();
2263            // We don't care which Layout Editor panel the signal mast is on, just so long as
2264            // the routing is done via layout blocks.
2265            remoteProtectingBlock = null;
2266            for (int i = 0; i < layout.size(); i++) {
2267                log.debug("{} Layout name {}", destinationSignalMast.getDisplayName(), editor );
2268                if (facingBlock == null) {
2269                    facingBlock = lbm.getFacingBlockByNamedBean(getSourceMast(), editor);
2270                }
2271                if (protectingBlock == null && protectingBlocks.isEmpty()) {
2272                    //This is wrong
2273                    protectingBlocks = lbm.getProtectingBlocksByNamedBean(getSourceMast(), editor);
2274                }
2275                if (destinationBlock == null) {
2276                    destinationBlock = lbm.getFacingBlockByNamedBean(destinationSignalMast, editor);
2277                }
2278                if (remoteProtectingBlock == null) {
2279                    remoteProtectingBlock = lbm.getProtectedBlockByNamedBean(destinationSignalMast, editor);
2280                }
2281            }
2282            // At this point, if we are not using the Layout Editor turnout or block
2283            // details then there is no point in trying to gather them.
2284            if ((!useLayoutEditorTurnouts) && (!useLayoutEditorBlocks)) {
2285                return;
2286            }
2287            if (facingBlock == null) {
2288                log.error("No facing block found for source mast {}", getSourceMast().getDisplayName());
2289                throw new JmriException("No facing block found for source mast " + getSourceMast().getDisplayName());
2290            }
2291            if (destinationBlock == null) {
2292                log.error("No facing block found for destination mast {}", destinationSignalMast.getDisplayName());
2293                throw new JmriException("No facing block found for destination mast " + destinationSignalMast.getDisplayName());
2294            }
2295            List<LayoutBlock> lblks = new ArrayList<>();
2296            if (protectingBlock == null) {
2297                log.debug("protecting block is null");
2298                String pBlkNames = "";
2299                StringBuffer lBlksNamesBuf = new StringBuffer();
2300                for (LayoutBlock pBlk : protectingBlocks) {
2301                    log.debug("checking layoutBlock {}", pBlk.getDisplayName());
2302                    pBlkNames = pBlkNames + pBlk.getDisplayName() + " (" + lbm.getLayoutBlockConnectivityTools().checkValidDest(facingBlock, pBlk, destinationBlock, remoteProtectingBlock, LayoutBlockConnectivityTools.Routing.MASTTOMAST) + "), ";
2303                    if (lbm.getLayoutBlockConnectivityTools().checkValidDest(facingBlock, pBlk, destinationBlock, remoteProtectingBlock, LayoutBlockConnectivityTools.Routing.MASTTOMAST)) {
2304                        try {
2305                            lblks = lbm.getLayoutBlockConnectivityTools().getLayoutBlocks(facingBlock, destinationBlock, pBlk, true, LayoutBlockConnectivityTools.Routing.MASTTOMAST);
2306                            protectingBlock = pBlk;
2307                            log.debug("building path names...");
2308                            for (LayoutBlock lBlk : lblks) {
2309                                lBlksNamesBuf.append(" ");
2310                                lBlksNamesBuf.append(lBlk.getDisplayName());
2311                            }
2312                            break;
2313                        } catch (JmriException ee) {
2314                            log.debug("path not found this time");
2315                        }
2316                    }
2317                }
2318                String lBlksNames = new String(lBlksNamesBuf);
2319
2320                if (protectingBlock == null) {
2321                    throw new JmriException("Path not valid, protecting block is null. Protecting block: " + pBlkNames
2322                        + " not connected to " + facingBlock.getDisplayName() + ". Layout block names: " + lBlksNames);
2323                }
2324            }
2325            if (!lbm.getLayoutBlockConnectivityTools().checkValidDest(facingBlock,protectingBlock,
2326                destinationBlock, remoteProtectingBlock, LayoutBlockConnectivityTools.Routing.MASTTOMAST)) {
2327                throw new JmriException("Path not valid, destination check failed.");
2328            }
2329            if (log.isDebugEnabled()) {
2330                log.debug("{} face {}", destinationSignalMast.getDisplayName(), facingBlock);
2331                log.debug("{} prot {}", destinationSignalMast.getDisplayName(), protectingBlock);
2332                log.debug("{} dest {}", destinationSignalMast.getDisplayName(), destinationBlock);
2333            }
2334
2335            if (destinationBlock != null && protectingBlock != null && facingBlock != null) {
2336                setAutoMasts(null, true);
2337                if (log.isDebugEnabled()) {
2338                    log.debug("{} face {}", destinationSignalMast.getDisplayName(), facingBlock.getDisplayName());
2339                    log.debug("{} prot {}", destinationSignalMast.getDisplayName(), protectingBlock.getDisplayName());
2340                    log.debug("{} dest {}", destinationSignalMast.getDisplayName(), destinationBlock.getDisplayName());
2341                }
2342
2343                try {
2344                    lblks = lbm.getLayoutBlockConnectivityTools().getLayoutBlocks(
2345                        facingBlock, destinationBlock, protectingBlock,
2346                            true, LayoutBlockConnectivityTools.Routing.MASTTOMAST);
2347                } catch (JmriException ee) {
2348                    log.error("No blocks found by the layout editor for pair {}-{}",
2349                        source.getDisplayName(), destinationSignalMast.getDisplayName());
2350                }
2351                LinkedHashMap<Block, Integer> block = setupLayoutEditorTurnoutDetails(lblks);
2352
2353                for (int i = 0; i < blockInXings.size(); i++) {
2354                    blockInXings.get(i).removeSignalMastLogic(source);
2355                }
2356                blockInXings = new ArrayList<>(0);
2357                xingAutoBlocks = new ArrayList<>(0);
2358                for (LayoutEditor lay : layout) {
2359                    for (LevelXing levelXing : lay.getLevelXings()) {
2360                        //Looking for a crossing that both layout blocks defined and they are individual.
2361                        if ((levelXing.getLayoutBlockAC() != null)
2362                                && (levelXing.getLayoutBlockBD() != null)
2363                                && (levelXing.getLayoutBlockAC() != levelXing.getLayoutBlockBD())) {
2364                            if (lblks.contains(levelXing.getLayoutBlockAC()) &&
2365                                    levelXing.getLayoutBlockAC() != facingBlock) {  // Don't include the facing xing blocks
2366                                block.put(levelXing.getLayoutBlockBD().getBlock(), Block.UNOCCUPIED);
2367                                xingAutoBlocks.add(levelXing.getLayoutBlockBD().getBlock());
2368                                blockInXings.add(levelXing);
2369                            } else if (lblks.contains(levelXing.getLayoutBlockBD()) &&
2370                                    levelXing.getLayoutBlockBD() != facingBlock) {  // Don't include the facing xing blocks
2371                                block.put(levelXing.getLayoutBlockAC().getBlock(), Block.UNOCCUPIED);
2372                                xingAutoBlocks.add(levelXing.getLayoutBlockAC().getBlock());
2373                                blockInXings.add(levelXing);
2374                            }
2375                        }
2376                    }
2377                }
2378                if (useLayoutEditorBlocks) {
2379                    setAutoBlocks(block);
2380                } else {
2381                    setAutoBlocks(null);
2382                }
2383                if (!useLayoutEditorTurnouts) {
2384                    setAutoTurnouts(null);
2385                }
2386
2387                setupAutoSignalMast(null, false);
2388            }
2389            initialise();
2390        }
2391
2392        /**
2393         * From a list of Layout Blocks, search for included Turnouts and their
2394         * Set To state.
2395         *
2396         * @param lblks List of Layout Blocks
2397         * @return a list of block - turnout state pairs
2398         */
2399        LinkedHashMap<Block, Integer> setupLayoutEditorTurnoutDetails(List<LayoutBlock> lblks) {
2400            ConnectivityUtil connection;
2401            List<LayoutTrackExpectedState<LayoutTurnout>> turnoutList;
2402            Hashtable<Turnout, Integer> turnoutSettings = new Hashtable<>();
2403            LinkedHashMap<Block, Integer> block = new LinkedHashMap<>();
2404            for (int i = 0; i < lblks.size(); i++) {
2405                log.debug("layoutblock {}",lblks.get(i).getDisplayName());
2406                block.put(lblks.get(i).getBlock(), Block.UNOCCUPIED);
2407                if ((i > 0)) {
2408                    int nxtBlk = i + 1;
2409                    int preBlk = i - 1;
2410                    if (i == lblks.size() - 1) {
2411                        nxtBlk = i;
2412                    }
2413                    //We use the best connectivity for the current block.
2414                    connection = new ConnectivityUtil(lblks.get(i).getMaxConnectedPanel());
2415                    if (i == lblks.size() - 1 && remoteProtectingBlock != null) {
2416                        turnoutList = connection.getTurnoutList(lblks.get(i)
2417                            .getBlock(), lblks.get(preBlk).getBlock(), remoteProtectingBlock.getBlock());
2418                    }else{
2419                        turnoutList = connection.getTurnoutList(lblks.get(i)
2420                            .getBlock(), lblks.get(preBlk).getBlock(), lblks.get(nxtBlk).getBlock());
2421                    }
2422                    for (int x = 0; x < turnoutList.size(); x++) {
2423                        LayoutTurnout lt = turnoutList.get(x).getObject();
2424                        if (lt instanceof LayoutSlip) {
2425                            LayoutSlip ls = (LayoutSlip) lt;
2426                            int slipState = turnoutList.get(x).getExpectedState();
2427                            int taState = ls.getTurnoutState(slipState);
2428                            Turnout tTemp = ls.getTurnout();
2429                            if (tTemp == null ) {
2430                                log.error("Unexpected null Turnout in {}, skipped", ls);
2431                                continue; // skip this one in loop, what else can you do?
2432                            }
2433                            turnoutSettings.put(ls.getTurnout(), taState);
2434                            int tbState = ls.getTurnoutBState(slipState);
2435                            turnoutSettings.put(ls.getTurnoutB(), tbState);
2436                        } else if ( lt != null ) {
2437                            String t = lt.getTurnoutName();
2438                            // temporary = why is this looking up the Turnout instead of using getTurnout()?
2439                            Turnout turnout = InstanceManager.turnoutManagerInstance().getTurnout(t);
2440                            if (log.isDebugEnabled()) {
2441                                if (    (lt.getTurnoutType() == LayoutTurnout.TurnoutType.RH_TURNOUT ||
2442                                         lt.getTurnoutType() == LayoutTurnout.TurnoutType.LH_TURNOUT ||
2443                                         lt.getTurnoutType() == LayoutTurnout.TurnoutType.WYE_TURNOUT)
2444                                        && (!lt.getBlockName().isEmpty())) {
2445                                    log.debug("turnout in list is straight left/right wye");
2446                                    log.debug("turnout block Name {}", lt.getBlockName());
2447                                    log.debug("current {} - pre {}", lblks.get(i).getBlock().getDisplayName(), lblks.get(preBlk).getBlock().getDisplayName());
2448                                    log.debug("A {}", lt.getConnectA());
2449                                    log.debug("B {}", lt.getConnectB());
2450                                    log.debug("C {}", lt.getConnectC());
2451                                    log.debug("D {}", lt.getConnectD());
2452                                }
2453                            }
2454                            if (turnout != null ) {
2455                                turnoutSettings.put(turnout, turnoutList.get(x).getExpectedState());
2456                            }
2457                            Turnout tempT;
2458                            if ((tempT = lt.getSecondTurnout()) != null) {
2459                                turnoutSettings.put(tempT, turnoutList.get(x).getExpectedState());
2460                            }
2461                            /* TODO: We could do with a more intelligent way to deal with double crossovers, other than
2462                                just looking at the state of the other conflicting blocks, such as looking at Signalmasts
2463                                that protect the other blocks and the settings of any other turnouts along the way.
2464                             */
2465                            if (lt.getTurnoutType() == LayoutTurnout.TurnoutType.DOUBLE_XOVER) {
2466                                LayoutBlock tempLB;
2467                                if (turnoutList.get(x).getExpectedState() == Turnout.THROWN) {
2468                                    if (lt.getLayoutBlock() == lblks.get(i) || lt.getLayoutBlockC() == lblks.get(i)) {
2469                                        // A or C, add B and D to remove list unless A=B or C=D
2470                                        if ((tempLB = lt.getLayoutBlockB()) != null) {
2471                                            if (!tempLB.equals(lt.getLayoutBlock())) {
2472                                                dblCrossoverAutoBlocks.add(tempLB.getBlock());
2473                                            }
2474                                            block.put(tempLB.getBlock(), Block.UNOCCUPIED);
2475                                        }
2476                                        if ((tempLB = lt.getLayoutBlockD()) != null) {
2477                                            if (!tempLB.equals(lt.getLayoutBlockC())) {
2478                                                dblCrossoverAutoBlocks.add(tempLB.getBlock());
2479                                            }
2480                                            block.put(tempLB.getBlock(), Block.UNOCCUPIED);
2481                                        }
2482                                    } else if (lt.getLayoutBlockB() == lblks.get(i) || lt.getLayoutBlockD() == lblks.get(i)) {
2483                                        // B or D, add A and C to remove list unless A=B or C=D
2484                                        if ((tempLB = lt.getLayoutBlock()) != null) {
2485                                            if (!tempLB.equals(lt.getLayoutBlockB())) {
2486                                                dblCrossoverAutoBlocks.add(tempLB.getBlock());
2487                                            }
2488                                            block.put(tempLB.getBlock(), Block.UNOCCUPIED);
2489                                        }
2490                                        if ((tempLB = lt.getLayoutBlockC()) != null) {
2491                                            if (!tempLB.equals(lt.getLayoutBlockD())) {
2492                                                dblCrossoverAutoBlocks.add(tempLB.getBlock());
2493                                            }
2494                                            block.put(tempLB.getBlock(), Block.UNOCCUPIED);
2495                                        }
2496                                    }
2497                                }
2498                            }
2499                        }
2500                    }
2501                }
2502            }
2503            // ----- Begin Turntable Alignment Check -----
2504            // For paths onto a turntable, we must add a condition for the turntable's alignment. The
2505            // turnout that controls the turntable position is associated with the specific ray track.
2506            // Each ray track can have a control turnout assigned. Setting this turnout to a specific
2507            // state (usually THROWN) is the command to align the turntable to that ray.
2508            // The following logic adds a CONDITION to the Signal Mast Logic, requiring that the
2509            // correct ray's turnout is in its required state before the signal will clear.
2510            // This does NOT command the turnout to move; it only checks its state for the interlocking.
2511            Set<LayoutEditor> layout = InstanceManager.getDefault(EditorManager.class).getAll(LayoutEditor.class);
2512            for (LayoutEditor lay : layout) {
2513                for (LayoutTurntable turntable : lay.getLayoutTurntables()) {
2514                    // Check for a path from an Approach Mast to the Buffer Mast (Path 3).
2515                    // The destination block is the turntable block, and the destination mast is the buffer mast.
2516                    if (turntable.getLayoutBlock() == destinationBlock && turntable.getBufferMast() == destinationSignalMast) {
2517                        // The source mast's facing block is the ray block for this path.
2518                        for (LayoutTurntable.RayTrack ray : turntable.getRayTrackList()) {
2519                            if (ray.getConnect() != null && ray.getConnect().getLayoutBlock() == DefaultSignalMastLogic.this.facingBlock) {
2520                                // This is the correct ray. Get its control turnout and required state.
2521                                Turnout rayTurnout = ray.getTurnout();
2522                                int requiredState = ray.getTurnoutState();
2523                                if (rayTurnout != null) {
2524                                    turnoutSettings.put(rayTurnout, requiredState);
2525                                }
2526                                break; // Found the ray, no need to check others.
2527                            }
2528                        }
2529                    }
2530                    // Check for a path from the Exit Mast to a remote mast (Path 1).
2531                    // The source mast is the turntable's exit mast.
2532                    if (turntable.getExitSignalMast() == getSourceMast()) {
2533                        // The protecting block is the ray block for this path.
2534                        for (LayoutTurntable.RayTrack ray : turntable.getRayTrackList()) {
2535                            if (ray.getConnect() != null && ray.getConnect().getLayoutBlock() == protectingBlock) {
2536                                // This is the correct ray. Get its control turnout and required state.
2537                                Turnout rayTurnout = ray.getTurnout();
2538                                int requiredState = ray.getTurnoutState();
2539                                if (rayTurnout != null) {
2540                                    turnoutSettings.put(rayTurnout, requiredState);
2541                                }
2542                                break; // Found the ray, no need to check others.
2543                            }
2544                        }
2545                    }
2546                }
2547            }
2548            // ----- End Turntable Alignment Check -----
2549            if (useLayoutEditorTurnouts) {
2550                setAutoTurnouts(turnoutSettings);
2551            }
2552            return block;
2553        }
2554
2555        /**
2556         * Generate auto signalmast for a given SML. Looks through all the other
2557         * logics to see if there are any blocks that are in common and thus
2558         * will add the other signal mast protecting that block.
2559         *
2560         * @param sml       The Signal Mast Logic for which to set up
2561         *                  autoSignalMasts
2562         * @param overwrite When true, replace an existing autoMasts list in the
2563         *                  SML
2564         */
2565        void setupAutoSignalMast(SignalMastLogic sml, boolean overwrite) {
2566            if (!allowAutoSignalMastGeneration) {
2567                return;
2568            }
2569            List<SignalMastLogic> smlList = InstanceManager.getDefault(SignalMastLogicManager.class)
2570                .getLogicsByDestination(destinationSignalMast);
2571            List<Block> allBlock = new ArrayList<>();
2572
2573            userSetBlocks.forEach(nbh -> allBlock.add((Block) nbh.getBean()));
2574
2575            Set<Block> blockKeys = autoBlocks.keySet();
2576            blockKeys.stream().filter(key -> (!allBlock.contains(key))).forEachOrdered(key ->
2577                allBlock.add(key));
2578            Hashtable<SignalMast, String> masts;
2579            if (sml != null) {
2580                masts = autoMasts;
2581                if (sml.areBlocksIncluded(allBlock)) {
2582                    SignalMast mast = sml.getSourceMast();
2583                    String danger = mast.getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.DANGER);
2584                    masts.put(mast, danger);
2585                } else {
2586                    //No change so will leave.
2587                    return;
2588                }
2589            } else {
2590                masts = new Hashtable<>();
2591                for (int i = 0; i < smlList.size(); i++) {
2592                    if (smlList.get(i).areBlocksIncluded(allBlock)) {
2593                        SignalMast mast = smlList.get(i).getSourceMast();
2594                        String danger = mast.getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.DANGER);
2595                        masts.put(mast, danger);
2596                    }
2597                }
2598            }
2599            setAutoMasts(masts, overwrite);
2600        }
2601
2602        /**
2603         * Add a certain Signal Mast to the list of AutoSignalMasts for this
2604         * SML.
2605         *
2606         * @param mast The Signal Mast to be added
2607         */
2608        void addAutoSignalMast(SignalMast mast) {
2609            log.debug("{} add mast to auto list {}", destinationSignalMast.getDisplayName(), mast);
2610            String danger = mast.getAppearanceMap().getSpecificAppearance(SignalAppearanceMap.DANGER);
2611            if (danger == null) {
2612                log.error("Can not add SignalMast {} to logic for {} to {} "
2613                    + "as it does not have a Danger appearance configured",
2614                        mast.getDisplayName(), source.getDisplayName(), destinationSignalMast.getDisplayName());
2615                return;
2616            }
2617            this.autoMasts.put(mast, danger);
2618            if (destMastInit) {
2619                mast.addPropertyChangeListener(propertySignalMastListener);
2620            }
2621            firePropertyChange(PROPERTY_AUTO_MASTS, null, this.destinationSignalMast);
2622        }
2623
2624        /**
2625         * Remove a certain Signal Mast from the list of AutoSignalMasts for
2626         * this SML.
2627         *
2628         * @param mast The Signal Mast to be removed
2629         */
2630        void removeAutoSignalMast(SignalMast mast) {
2631            this.autoMasts.remove(mast);
2632            if (destMastInit) {
2633                mast.removePropertyChangeListener(propertySignalMastListener);
2634            }
2635            firePropertyChange(PROPERTY_AUTO_MASTS, this.destinationSignalMast, null);
2636        }
2637
2638        boolean useLayoutEditor() {
2639            return useLayoutEditor;
2640        }
2641
2642        boolean useLayoutEditorBlocks() {
2643            return useLayoutEditorBlocks;
2644        }
2645
2646        boolean useLayoutEditorTurnouts() {
2647            return useLayoutEditorTurnouts;
2648        }
2649
2650        boolean allowAutoSignalMastGeneration = false;
2651
2652        boolean allowAutoSignalMastGen() {
2653            return allowAutoSignalMastGeneration;
2654        }
2655
2656        void allowAutoSignalMastGen(boolean gen) {
2657            if (allowAutoSignalMastGeneration == gen) {
2658                return;
2659            }
2660            allowAutoSignalMastGeneration = gen;
2661        }
2662
2663        /**
2664         * Remove references from this Destination Mast and clear lists, so that
2665         * it can eventually be garbage-collected.
2666         * <p>
2667         * Note: This does not stop any delayed operations that might be queued.
2668         */
2669        void dispose() {
2670            disposed = true;
2671            clearTurnoutLock();
2672            destinationSignalMast.removePropertyChangeListener(propertyDestinationMastListener);
2673            setBlocks(null);
2674            setAutoBlocks(null);
2675            setTurnouts(null);
2676            setAutoTurnouts(null);
2677            setSensors(null);
2678            setMasts(null);
2679            setAutoMasts(null, true);
2680        }
2681
2682        void lockTurnouts() {
2683            // We do not allow the turnouts to be locked if we are disposing the logic,
2684            // if the logic is not active, or if we do not allow the turnouts to be locked.
2685            if ((disposed) || (!lockTurnouts) || (!active)) {
2686                return;
2687            }
2688
2689            userSetTurnouts.stream().map(nbh -> (Turnout) nbh.getBean()).forEachOrdered(key -> {
2690                key.setLocked(Turnout.CABLOCKOUT + Turnout.PUSHBUTTONLOCKOUT, true);
2691            });
2692            Enumeration<Turnout> keys = autoTurnouts.keys();
2693            while (keys.hasMoreElements()) {
2694                Turnout key = keys.nextElement();
2695                key.setLocked(Turnout.CABLOCKOUT + Turnout.PUSHBUTTONLOCKOUT, true);
2696            }
2697        }
2698
2699        void clearTurnoutLock() {
2700            // We do not allow the turnout lock to be cleared if we are not active,
2701            // and the lock flag has not been set.
2702            if ((!lockTurnouts) && (!active)) {
2703                return;
2704            }
2705
2706            Enumeration<Turnout> keys = autoTurnouts.keys();
2707            while (keys.hasMoreElements()) {
2708                Turnout key = keys.nextElement();
2709                key.setLocked(Turnout.CABLOCKOUT + Turnout.PUSHBUTTONLOCKOUT, false);
2710            }
2711
2712            userSetTurnouts.stream().map(nbh -> (Turnout) nbh.getBean()).forEachOrdered(key ->
2713                key.setLocked(Turnout.CABLOCKOUT + Turnout.PUSHBUTTONLOCKOUT, false));
2714        }
2715
2716        protected void calculateSpeed() {
2717            log.debug("{} calculate the speed setting for this logic ie what the signalmast will display", destinationSignalMast.getDisplayName());
2718            minimumBlockSpeed = 0.0f;
2719            Enumeration<Turnout> keys = autoTurnouts.keys();
2720            while (keys.hasMoreElements()) {
2721                Turnout key = keys.nextElement();
2722                log.debug("{} turnout {}", destinationSignalMast.getDisplayName(), key.getDisplayName());
2723                if (!isTurnoutIncluded(key)) {
2724                    if (autoTurnouts.get(key) == Turnout.CLOSED) {
2725                        if (((key.getStraightLimit() < minimumBlockSpeed) || (minimumBlockSpeed == 0)) && (key.getStraightLimit() != -1)) {
2726                            minimumBlockSpeed = key.getStraightLimit();
2727                            log.debug("{} turnout {} set speed to {}", destinationSignalMast.getDisplayName(), key.getDisplayName(), minimumBlockSpeed);
2728                        }
2729                    } else {
2730                        if (((key.getDivergingLimit() < minimumBlockSpeed) || (minimumBlockSpeed == 0)) && (key.getDivergingLimit() != -1)) {
2731                            minimumBlockSpeed = key.getDivergingLimit();
2732                            log.debug("{} turnout {} set speed to {}", destinationSignalMast.getDisplayName(), key.getDisplayName(), minimumBlockSpeed);
2733                        }
2734                    }
2735                }
2736            }
2737
2738            userSetTurnouts.forEach(nbh -> {
2739                Turnout key = (Turnout) nbh.getBean();
2740                if (nbh.getSetting() == Turnout.CLOSED) {
2741                    if (((key.getStraightLimit() < minimumBlockSpeed) || (minimumBlockSpeed == 0)) && (key.getStraightLimit() != -1)) {
2742                        minimumBlockSpeed = key.getStraightLimit();
2743                        log.debug("{} turnout {} set speed to {}", destinationSignalMast.getDisplayName(), key.getDisplayName(), minimumBlockSpeed);
2744                    }
2745                } else if (nbh.getSetting() == Turnout.THROWN) {
2746                    if (((key.getDivergingLimit() < minimumBlockSpeed) || (minimumBlockSpeed == 0)) && (key.getDivergingLimit() != -1)) {
2747                        minimumBlockSpeed = key.getDivergingLimit();
2748                        log.debug("{} turnout {} set speed to {}", destinationSignalMast.getDisplayName(), key.getDisplayName(), minimumBlockSpeed);
2749                    }
2750                }
2751            });
2752
2753            Set<Block> autoBlockKeys = autoBlocks.keySet();
2754            for (Block key : autoBlockKeys) {
2755                log.debug("{} auto block add list {}", destinationSignalMast.getDisplayName(), key.getDisplayName());
2756                if (!isBlockIncluded(key)) {
2757                    if (((key.getSpeedLimit() < minimumBlockSpeed) || (minimumBlockSpeed == 0)) && (key.getSpeedLimit() != -1)) {
2758                        minimumBlockSpeed = key.getSpeedLimit();
2759                        log.debug("{} block {} set speed to {}", destinationSignalMast.getDisplayName(), key.getDisplayName(), minimumBlockSpeed);
2760                    }
2761                }
2762            }
2763            for (NamedBeanSetting nbh : userSetBlocks) {
2764                Block key = (Block) nbh.getBean();
2765                if (((key.getSpeedLimit() < minimumBlockSpeed) || (minimumBlockSpeed == 0)) && (key.getSpeedLimit() != -1)) {
2766                    log.debug("{} block {} set speed to {}", destinationSignalMast.getDisplayName(), key.getDisplayName(), minimumBlockSpeed);
2767                    minimumBlockSpeed = key.getSpeedLimit();
2768                }
2769            }
2770            /*if(minimumBlockSpeed==-0.1f)
2771             minimumBlockSpeed==0.0f;*/
2772        }
2773
2774        protected PropertyChangeListener propertySensorListener = new PropertyChangeListener() {
2775            @Override
2776            public void propertyChange(PropertyChangeEvent e) {
2777                Sensor sen = (Sensor) e.getSource();
2778                log.debug("{} to {} destination sensor {} trigger {}",source.getDisplayName(), destinationSignalMast.getDisplayName(), sen.getDisplayName(), e.getPropertyName());
2779                if ( Sensor.PROPERTY_KNOWN_STATE.equals(e.getPropertyName())) {
2780                    int now = ((Integer) e.getNewValue());
2781                    log.debug("current value {} value we want {}", now, getSensorState(sen));
2782                    if (isSensorIncluded(sen) && getSensorState(sen) != now) {
2783                        log.debug("Sensor {} caused the signalmast to be set to danger", sen.getDisplayName());
2784                        //getSourceMast().setAspect(stopAspect);
2785                        if (active == true) {
2786                            active = false;
2787                            setSignalAppearance();
2788                        }
2789                    } else if (getSensorState(sen) == now) {
2790                        log.debug("{} sensor {} triggers a calculation of change", destinationSignalMast.getDisplayName(), sen.getDisplayName());
2791                        checkState();
2792                    }
2793                }
2794            }
2795        };
2796
2797        protected PropertyChangeListener propertyTurnoutListener = new PropertyChangeListener() {
2798            @Override
2799            public void propertyChange(PropertyChangeEvent e) {
2800                Turnout turn = (Turnout) e.getSource();
2801                //   log.debug(destination.getDisplayName() + " destination sensor "+ sen.getDisplayName() + "trigger");
2802                if ( Turnout.PROPERTY_KNOWN_STATE.equals(e.getPropertyName())) {
2803                    //Need to check this against the manual list vs auto list
2804                    //The manual list should over-ride the auto list
2805                    int now = ((Integer) e.getNewValue());
2806                    if (isTurnoutIncluded(turn)) {
2807                        if (getTurnoutState(turn) != now) {
2808                            log.debug("Turnout {} caused the signalmast to be set", turn.getDisplayName());
2809                            log.debug("From {} to {} Turnout {} caused the signalmast to be set to danger", getSourceMast().getDisplayName(), destinationSignalMast.getDisplayName(), turn.getDisplayName());
2810                            if (active == true) {
2811                                active = false;
2812                                setSignalAppearance();
2813                            }
2814                        } else {
2815                            log.debug("{} turnout {} triggers a calculation of change", destinationSignalMast.getDisplayName(), turn.getDisplayName());
2816                            checkState();
2817                        }
2818                    } else if (autoTurnouts.containsKey(turn)) {
2819                        if (getAutoTurnoutState(turn) != now) {
2820                            log.debug("Turnout {} auto caused the signalmast to be set", turn.getDisplayName());
2821                            log.debug("From {} to {} Auto Turnout {} auto caused the signalmast to be set to danger", getSourceMast().getDisplayName(), destinationSignalMast.getDisplayName(), turn.getDisplayName());
2822                            if (active == true) {
2823                                active = false;
2824                                setSignalAppearance();
2825                            }
2826                        } else {
2827                            log.debug("From {} to {} turnout {} triggers a calculation of change", getSourceMast().getDisplayName(), destinationSignalMast.getDisplayName(), turn.getDisplayName());
2828                            checkState();
2829                        }
2830                    }
2831
2832                } else if ( Turnout.PROPERTY_TURNOUT_STRAIGHT_SPEED.equals(e.getPropertyName())
2833                        || Turnout.PROPERTY_TURNOUT_DIVERGING_SPEED.equals(e.getPropertyName())) {
2834                    calculateSpeed();
2835                }
2836            }
2837        };
2838
2839        protected PropertyChangeListener propertyBlockListener = new PropertyChangeListener() {
2840            @Override
2841            public void propertyChange(PropertyChangeEvent e) {
2842                Block block = (Block) e.getSource();
2843                log.debug("{} destination block {} trigger {} {}", destinationSignalMast.getDisplayName(), block.getDisplayName(), e.getPropertyName(), e.getNewValue());
2844                if ( Block.PROPERTY_STATE.equals(e.getPropertyName()) || Block.PROPERTY_ALLOCATED.equals(e.getPropertyName())) {
2845                    // TODO: what is this?
2846                    log.debug("Included in user entered block {}", Boolean.toString(isBlockIncluded(block)));
2847                    log.debug("Included in AutoGenerated Block {}", Boolean.toString(autoBlocks.containsKey(block)));
2848                    if (isBlockIncluded(block)) {
2849                        log.debug("{} in manual block", destinationSignalMast.getDisplayName());
2850                        log.debug("  state: {}  {}", getBlockState(block), block.getState());
2851                        checkState();
2852                    } else if (autoBlocks.containsKey(block)) {
2853                        log.debug("{} in auto block", destinationSignalMast.getDisplayName());
2854                        log.debug("  states: {}  {}", getAutoBlockState(block), block.getState());
2855                        checkState();
2856                    } else {
2857                        log.debug("{} Not found", destinationSignalMast.getDisplayName());
2858                    }
2859                } else if ( e.getPropertyName().equals("BlockSpeedChange")) {
2860                    calculateSpeed();
2861                }
2862            }
2863        };
2864
2865        protected PropertyChangeListener propertySignalMastListener = new PropertyChangeListener() {
2866            @Override
2867            public void propertyChange(PropertyChangeEvent e) {
2868
2869                SignalMast mast = (SignalMast) e.getSource();
2870                log.debug("{} signalmast change {} {}", destinationSignalMast.getDisplayName(), mast.getDisplayName(), e.getPropertyName());
2871                //   log.debug(destination.getDisplayName() + " destination sensor "+ sen.getDisplayName() + "trigger");
2872                if ( SignalMast.PROPERTY_ASPECT.equals(e.getPropertyName())) {
2873
2874                    String now = ((String) e.getNewValue());
2875                    log.debug("{} match property {}", destinationSignalMast.getDisplayName(), now);
2876                    if (isSignalMastIncluded(mast)) {
2877                        if (!now.equals(getSignalMastState(mast))) {
2878                            log.debug("{} in mast list SignalMast {} caused the signalmast to be set", destinationSignalMast.getDisplayName(), mast.getDisplayName());
2879                            log.debug("SignalMast {} caused the signalmast to be set", mast.getDisplayName());
2880                            if (active) {
2881                                active = false;
2882                                setSignalAppearance();
2883                            }
2884                        } else {
2885                            log.debug("{} in mast list signalmast change", destinationSignalMast.getDisplayName());
2886                            checkState();
2887                        }
2888                    } else if (autoMasts.containsKey(mast)) {
2889                        if (!now.equals(getAutoSignalMastState(mast))) {
2890                            log.debug("SignalMast {} caused the signalmast to be set", mast.getDisplayName());
2891                            log.debug("{} in auto mast list SignalMast {} caused the signalmast to be set", destinationSignalMast.getDisplayName(), mast.getDisplayName());
2892                            if (active) {
2893                                active = false;
2894                                setSignalAppearance();
2895                            }
2896                        } else {
2897                            log.debug("{} in auto mast list signalmast change", destinationSignalMast.getDisplayName());
2898                            checkState();
2899                        }
2900                    }
2901                }
2902            }
2903        };
2904
2905        private class NamedBeanSetting {
2906
2907            NamedBeanHandle<?> namedBean;
2908            int setting = 0;
2909            String strSetting = null;
2910
2911            NamedBeanSetting(NamedBeanHandle<?> namedBean, int setting) {
2912                this.namedBean = namedBean;
2913                this.setting = setting;
2914            }
2915
2916            NamedBeanSetting(NamedBeanHandle<?> namedBean, String setting) {
2917                this.namedBean = namedBean;
2918                strSetting = setting;
2919            }
2920
2921            NamedBean getBean() {
2922                return namedBean.getBean();
2923            }
2924
2925            NamedBeanHandle<?> getNamedBean() {
2926                return namedBean;
2927            }
2928
2929            int getSetting() {
2930                return setting;
2931            }
2932
2933            String getStringSetting() {
2934                return strSetting;
2935            }
2936
2937            String getBeanName() {
2938                return namedBean.getName();
2939            }
2940        }
2941    }
2942
2943    /**
2944     * The listener on the destination Signal Mast.
2945     */
2946    private PropertyChangeListener propertyDestinationMastListener = new PropertyChangeListener() {
2947        @Override
2948        public void propertyChange(PropertyChangeEvent e) {
2949            SignalMast mast = (SignalMast) e.getSource();
2950            if (mast == destination) {
2951                log.debug("destination mast change {}", mast.getDisplayName());
2952                setSignalAppearance();
2953            }
2954        }
2955    };
2956
2957    /**
2958     * The listener on the source Signal Mast.
2959     */
2960    private PropertyChangeListener propertySourceMastListener = new PropertyChangeListener() {
2961        @Override
2962        public void propertyChange(PropertyChangeEvent e) {
2963            SignalMast mast = (SignalMast) e.getSource();
2964            if ((mast == source) && ( SignalMast.PROPERTY_HELD.equals(e.getPropertyName()))) {
2965                log.debug("source mast change {} {}", mast.getDisplayName(), e.getPropertyName());
2966                setSignalAppearance();
2967            }
2968        }
2969    };
2970
2971    //@todo need to think how we deal with auto generated lists based upon the layout editor.
2972    @Override
2973    public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
2974        NamedBean nb = (NamedBean) evt.getOldValue();
2975        if (Manager.PROPERTY_CAN_DELETE.equals(evt.getPropertyName())) {
2976            boolean found = false;
2977            StringBuilder message = new StringBuilder();
2978            if (nb instanceof SignalMast) {
2979                if (nb.equals(source)) {
2980                    message.append("Has SignalMast Logic attached which will be <b>Deleted</b> to <ul>");
2981                    for (SignalMast sm : getDestinationList()) {
2982                        message.append("<li>");
2983                        message.append(sm.getDisplayName());
2984                        message.append("</li>");
2985                    }
2986                    message.append("</ul>");
2987                    throw new PropertyVetoException(message.toString(), evt);
2988
2989                } else if (isDestinationValid((SignalMast) nb)) {
2990                    throw new PropertyVetoException("Is the end point mast for logic attached to signal mast " + source.getDisplayName() + " which will be <b>Deleted</b> ", evt);
2991                }
2992                for (SignalMast sm : getDestinationList()) {
2993                    if (isSignalMastIncluded((SignalMast) nb, sm)) {
2994                        message.append("<li>");
2995                        message.append("Used in conflicting logic of ").append(source.getDisplayName())
2996                            .append(" & ").append(sm.getDisplayName()).append("</li>");
2997                    }
2998                }
2999            }
3000            if (nb instanceof Turnout) {
3001                for (SignalMast sm : getDestinationList()) {
3002                    if (isTurnoutIncluded((Turnout) nb, sm)) {
3003                        message.append("<li>Is in logic between Signal Masts ").append(source.getDisplayName())
3004                            .append(" ").append(sm.getDisplayName()).append("</li>");
3005                        found = true;
3006                    }
3007                }
3008            }
3009            if (nb instanceof Sensor) {
3010                for (SignalMast sm : getDestinationList()) {
3011                    if (isSensorIncluded((Sensor) nb, sm)) {
3012                        message.append("<li>");
3013                        message.append("Is in logic between Signal Masts ").append(source.getDisplayName())
3014                            .append(" ").append(sm.getDisplayName()).append("</li>");
3015                        found = true;
3016                    }
3017                }
3018            }
3019            if (found) {
3020                throw new PropertyVetoException(message.toString(), evt);
3021            }
3022        } else if (Manager.PROPERTY_DO_DELETE.equals(evt.getPropertyName())) {
3023            if (nb instanceof SignalMast) {
3024                if (nb.equals(source)) {
3025                    dispose();
3026                }
3027                if (isDestinationValid((SignalMast) nb)) {
3028                    removeDestination((SignalMast) nb);
3029                }
3030                for (SignalMast sm : getDestinationList()) {
3031                    if (isSignalMastIncluded((SignalMast) nb, sm)) {
3032                        log.warn("Unhandled condition: signal mast included during DoDelete");
3033                        // @todo need to deal with this situation
3034                    }
3035                }
3036            }
3037            if (nb instanceof Turnout) {
3038                Turnout t = (Turnout) nb;
3039                getDestinationList().stream().filter(sm -> (isTurnoutIncluded(t, sm))).forEachOrdered(sm -> {
3040                    removeTurnout(t, sm);
3041                });
3042            }
3043            if (nb instanceof Sensor) {
3044                Sensor s = (Sensor) nb;
3045                getDestinationList().stream().filter(sm -> (isSensorIncluded(s, sm))).forEachOrdered(sm -> {
3046                    removeSensor(s, sm);
3047                });
3048            }
3049        }
3050    }
3051
3052    /**
3053     * Note: This does not stop any delayed operations that might be queued.
3054     */
3055    @Override
3056    public void dispose() {
3057        disposing = true;
3058        getSourceMast().removePropertyChangeListener(propertySourceMastListener);
3059        Enumeration<SignalMast> en = destList.keys();
3060        while (en.hasMoreElements()) {
3061            SignalMast dm = en.nextElement();
3062            destList.get(dm).dispose();
3063        }
3064        super.dispose(); // release any prop change listeners
3065    }
3066
3067    /**
3068     * {@inheritDoc }
3069     */
3070    @Override
3071    public String getBeanType() {
3072        return Bundle.getMessage("BeanNameSignalMastLogic");
3073    }
3074
3075    /**
3076     * No valid integer state, always return a constant.
3077     *
3078     * @return Always zero
3079     */
3080    @Override
3081    public int getState() {
3082        return 0;
3083    }
3084
3085    @Override
3086    public void setState(int i) {
3087    }
3088
3089    /**
3090     * {@inheritDoc }
3091     */
3092    @Override
3093    public List<NamedBeanUsageReport> getUsageReport(NamedBean bean) {
3094        List<NamedBeanUsageReport> report = new ArrayList<>();
3095        if (bean != null) {
3096            if (bean.equals(getSourceMast())) {
3097                report.add(new NamedBeanUsageReport("SMLSourceMast"));  // NOI18N
3098            }
3099            getDestinationList().forEach((dest) -> {
3100                if (bean.equals(dest)) {
3101                    report.add(new NamedBeanUsageReport("SMLDestinationMast"));  // NOI18N
3102                }
3103                getAutoBlocks(dest).forEach((block) -> {
3104                    if (bean.equals(block)) {
3105                        report.add(new NamedBeanUsageReport("SMLBlockAuto", dest));  // NOI18N
3106                    }
3107                });
3108                getBlocks(dest).forEach((block) -> {
3109                    if (bean.equals(block)) {
3110                        report.add(new NamedBeanUsageReport("SMLBlockUser", dest));  // NOI18N
3111                    }
3112                });
3113                getAutoTurnouts(dest).forEach((turnout) -> {
3114                    if (bean.equals(turnout)) {
3115                        report.add(new NamedBeanUsageReport("SMLTurnoutAuto", dest));  // NOI18N
3116                    }
3117                });
3118                getTurnouts(dest).forEach((turnout) -> {
3119                    if (bean.equals(turnout)) {
3120                        report.add(new NamedBeanUsageReport("SMLTurnoutUser", dest));  // NOI18N
3121                    }
3122                });
3123                getSensors(dest).forEach((sensor) -> {
3124                    if (bean.equals(sensor)) {
3125                        report.add(new NamedBeanUsageReport("SMLSensor", dest));  // NOI18N
3126                    }
3127                });
3128                getAutoMasts(dest).forEach((mast) -> {
3129                    if (bean.equals(mast)) {
3130                        report.add(new NamedBeanUsageReport("SMLMastAuto", dest));  // NOI18N
3131                    }
3132                });
3133                getSignalMasts(dest).forEach((mast) -> {
3134                    if (bean.equals(mast)) {
3135                        report.add(new NamedBeanUsageReport("SMLMastUser", dest));  // NOI18N
3136                    }
3137                });
3138            });
3139        }
3140        return report;
3141    }
3142
3143    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultSignalMastLogic.class);
3144
3145}