001package jmri.jmrit.dispatcher;
002
003import java.util.ArrayList;
004import java.util.HashMap;
005import java.util.Iterator;
006import java.util.List;
007import java.util.Map;
008import java.util.Map.Entry;
009import java.util.concurrent.LinkedBlockingQueue;
010
011import jmri.Block;
012import jmri.InstanceManager;
013import jmri.Section;
014import jmri.Sensor;
015import jmri.Transit;
016import jmri.TransitSection;
017import jmri.jmrit.dispatcher.TaskAllocateRelease.TaskAction;
018
019import org.slf4j.Logger;
020import org.slf4j.LoggerFactory;
021
022/**
023 * Handles automatic allocation of Sections for Dispatcher
024 * <p>
025 * AutoAllocate.java is an extension of DispatcherFrame.java.
026 * <p>
027 * When AutoAllocate is triggered, it scans the list of Allocation Requests, in
028 * order of the priorities of ActiveTrains with pending AllocationRequests,
029 * testing if a requested allocation can be made. AutoAllocate returns when
030 * either: A Section has been allocated -or- All AllocationRequests have been
031 * tested, and no allocation is indicated.
032 * <p>
033 * If AutoAllocate needs to save information related to a plan requiring
034 * multiple allocations, an AllocationPlan object is created. When the plan is
035 * complete, the AllocationPlan object is disposed of. Multiple AllocationPlan
036 * objects may be active at any one time.
037 * <p>
038 * AutoAllocate is triggered by each of the following events: An
039 * AllocatedSection has been released, freeing up a Section. A new
040 * AllocationRequest has been entered into the queue of AllocationRequests. A
041 * Section has been allocated, either by AutoAllocate or manually by the
042 * dispatcher.
043 * <p>
044 * AutoAllocate requires that AutoRelease is active.
045 * <p>
046 * AutoAllocate operates conservatively, that is, if there is any doubt that a
047 * Section should be allocated, it will not allocate the Section.
048 * <p>
049 * AutoAllocate develops plans for meets when multiple ActiveTrains are using
050 * the same Sections of track. These plans are automatically created and
051 * removed. They are stored in AllocationPlan objects to avoid having to
052 * continually recreate them, since the logic to create them is rather
053 * complicated.
054 * <p>
055 * The dispatcher is free to switch AutoAllocate on or off at any tine in
056 * DispatcherFrame. When AutoAllocate is switched off, all existing
057 * AllocationPlan objects are discarded.
058 * <p>
059 * All work done within the class is queued using a blocking queue. This is
060 * to ensure the integrity of arrays, both in Dispatcher and ActiveTrain,
061 * and to prevent calls within calls to modify those arrays, including autorelease.
062 * <br>
063 * <hr>
064 * This file is part of JMRI.
065 * <p>
066 * JMRI is free software; you can redistribute it and/or modify it under the
067 * terms of version 2 of the GNU General Public License as published by the Free
068 * Software Foundation. See the "COPYING" file for a copy of this license.
069 * <p>
070 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY
071 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
072 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
073 *
074 * @author Dave Duchamp Copyright (C) 2011
075 */
076public class AutoAllocate implements Runnable {
077
078    LinkedBlockingQueue<TaskAllocateRelease> taskList;
079
080    public AutoAllocate(DispatcherFrame d, List<AllocationRequest> inAllocationRequests) {
081        _dispatcher = d;
082        allocationRequests = inAllocationRequests;
083        if (_dispatcher == null) {
084            log.error("null DispatcherFrame when constructing AutoAllocate");
085            return;
086        }
087        taskList = new LinkedBlockingQueue<>();
088    }
089
090    // operational variables
091    private static final jmri.NamedBean.DisplayOptions USERSYS = jmri.NamedBean.DisplayOptions.USERNAME_SYSTEMNAME;
092    private DispatcherFrame _dispatcher = null;
093    private final List<AllocationPlan> _planList = new ArrayList<>();
094    private int nextPlanNum = 1;
095    private final List<AllocationRequest> orderedRequests = new ArrayList<>();
096    private List<AllocationRequest> allocationRequests = null;
097    private final Map<String, String> reservedSections = new HashMap<String, String>();
098
099    private boolean abort = false;
100
101    /**
102     * Stops the autoAllocate nicely
103     */
104    protected void setAbort() {
105        abort = true;
106        scanAllocationRequests(new TaskAllocateRelease(TaskAction.ABORT)); //force queue flush
107    }
108
109    /*
110     * return true when the taskList queue is Empty
111     */
112    protected boolean allRequestsDone() {
113        return taskList.isEmpty();
114    }
115
116    protected void scanAllocationRequests(TaskAllocateRelease task) {
117        log.trace("Add request from [{}] for [{}][{}]",
118                task.getTrainName(),task.getAction().name(),
119                task.getAllocationRequest() == null ? "None" : task.getAllocationRequest().getSectionName());
120        taskList.add(task);
121    }
122
123    /*
124     * Main loop processing queue
125     */
126    @Override
127    public void run() {
128        while (!abort) {
129            try {
130                TaskAllocateRelease task = taskList.take();
131                try {
132                    switch (task.getAction()) {
133                        case AUTO_RELEASE:
134                            _dispatcher.checkAutoRelease();
135                            break;
136                        case RELEASE_ONE:
137                            _dispatcher.doReleaseAllocatedSection(task.getAllocatedSection(),
138                                    task.getTerminatingTrain());
139                            break;
140                        case RELEASE_RESERVED:
141                            removeAllReservesForTrain(task.getTrainName());
142                            break;
143                        case SCAN_REQUESTS:
144                            scanAllocationRequestList(allocationRequests);
145                            break;
146                        case ALLOCATE_IMMEDIATE:
147                            _dispatcher.allocateSection(task.getAllocationRequest(), null);
148                            break;
149                        case ABORT:
150                            abort = true; //belt an braces
151                            break;
152                        default:
153                            log.error("Unknown action in TaskAllocateRelease - ignoring");
154                    }
155                } catch (Exception ex) {
156                    log.error("Unexpected Exeption, likely bad task request.", ex);
157                }
158            } catch (InterruptedException ex) {
159                log.error("Blocklist killed, taking this as terminate", ex);
160                abort = true;
161            }
162        }
163    }
164
165    /**
166     * This is the entry point to AutoAllocate when it is triggered.
167     *
168     * @param list list to scan
169     */
170    private synchronized void scanAllocationRequestList(List<AllocationRequest> list) {
171        boolean okToAllocate = false;
172        if (list.size() <= 0) {
173            return;
174        }
175        // copy AllocationRequests in order of priority of ActiveTrain.
176        copyAndSortARs(list);
177        removeCompletePlans();
178        for (int i = 0; i < orderedRequests.size(); i++) {
179            try {
180                okToAllocate = false;
181                AllocationRequest ar = orderedRequests.get(i);
182                if (ar == null) {
183                    log.error("error in allocation request list - AllocationRequest is null");
184                    continue;
185                }
186                // Check to see if there is a sensor temporarily block
187                // allocation blocking allocation
188                ActiveTrain activeTrain = ar.getActiveTrain();
189                String trainName = activeTrain.getTrainName();
190                log.trace("{}: try to allocate [{}]", trainName, ar.getSection().getDisplayName(USERSYS));
191                if (activeTrain.getLastAllocatedSection() != null) {
192                    // do stuff associated with the last allocated section
193                    Transit arTransit = activeTrain.getTransit();
194                    TransitSection arCurrentTransitSection =
195                            arTransit.getTransitSectionFromSectionAndSeq(activeTrain.getLastAllocatedSection(),
196                                    activeTrain.getLastAllocatedSectionSeqNumber());
197                    // stop allocating sensor active?
198                    if (stopAllocateSensorSet(activeTrain, arCurrentTransitSection)) {
199                        log.debug("[{}]:StopAllocateSensor active", trainName);
200                        continue;
201                    }
202                    // is the train held
203                    if (activeTrain.holdAllocation()|| (!activeTrain.getStarted()) && activeTrain.getDelayedStart() != ActiveTrain.NODELAY) {
204                        log.debug("[{}]:Allocation is Holding or Delayed hold[{}] started[{}], delayedstart[{}]", trainName, activeTrain.holdAllocation(), activeTrain.getStarted(), activeTrain.getDelayedStart() != ActiveTrain.NODELAY);
205                        continue;
206                    }
207                    // apparently holdAllocation() is not set when holding !!!
208                    if (InstanceManager.getDefault(DispatcherFrame.class)
209                            .getSignalType() == DispatcherFrame.SIGNALMAST &&
210                            isSignalHeldAtStartOfSection(ar)) {
211                        continue;
212                    }
213                    // this already reserved for the train, allocate.
214                    String reservedTrainName = reservedSections.get(ar.getSection().getSystemName());
215                    if (reservedTrainName != null) {
216                        if (reservedTrainName.equals(trainName)) {
217                            String sectionName = ar.getSection().getSystemName();
218                            if (allocateMore(ar)) {
219                                reservedSections.remove(sectionName);
220                            }
221                            continue;
222                        }
223                    }
224
225                    if (activeTrain.getAllocateMethod() == ActiveTrain.ALLOCATE_BY_SAFE_SECTIONS) {
226                        log.trace("{}: Allocating [{}] using Safe Sections", trainName,
227                                ar.getSection().getDisplayName());
228                        // if the last allocated section is safe but not
229                        // occupied short cut out of here
230                        if ( (activeTrain.getLastAllocOverrideSafe() == null ||
231                                    ( activeTrain.getLastAllocOverrideSafe() != arCurrentTransitSection.getSection()))
232                                && arCurrentTransitSection.isSafe()
233                                && activeTrain.getLastAllocatedSection().getOccupancy() != Section.OCCUPIED) {
234                            log.debug("Allocating Train [{}] has not arrived at Passing Point",
235                                    trainName);
236                            continue;
237                        }
238                        // Check all forward sections till a passing point.
239                        int itSequ = ar.getSectionSeqNumber();
240                        int iIncrement = 0;
241                        int iLimit = 0;
242                        int ix = 0;
243                        boolean skip = false;
244                        int iStart = 0;
245                        if (activeTrain.isTransitReversed()) {
246                            iIncrement = -1;
247                            iLimit = 0;
248                            iStart = itSequ; // reverse transits start
249                                             // allocating from the next
250                                             // one, they allocate the one
251                                             // there in already
252                        } else {
253                            if (activeTrain.getStartBlockSectionSequenceNumber() == ar.getSectionSeqNumber()) {
254                                skip = true;
255                            }
256                            iIncrement = +1;
257                            iLimit = arTransit.getMaxSequence() + 1;
258                            iStart = itSequ;
259                        }
260                        if (!skip) {
261                            boolean areForwardsFree = false;
262                            log.trace("index [{}] Limit [{}] transitsize [{}]", ix, iLimit,
263                                    arTransit.getTransitSectionList().size());
264                            for (ix = iStart; ix != iLimit; ix += iIncrement) {
265                                log.trace("index [{}] Limit [{}] transitsize [{}]", ix, iLimit,
266                                        arTransit.getTransitSectionList().size());
267                                // ensure all blocks section and blocks free
268                                // till next Passing Point, check alternates
269                                // if they exist.
270                                Section sS;
271                                ArrayList<TransitSection> sectionsInSeq = arTransit.getTransitSectionListBySeq(ix);
272                                areForwardsFree = false; // Posit will be
273                                                         // bad
274                                log.trace("Search ALternates Size[{}]", sectionsInSeq.size());
275                                int seqNumberfound = 0;
276                                for (int iSectionsInSeq = 0; iSectionsInSeq < sectionsInSeq.size() &&
277                                        !areForwardsFree; iSectionsInSeq++) {
278                                    log.trace("iSectionInSeq[{}]", iSectionsInSeq);
279                                    sS = sectionsInSeq.get(iSectionsInSeq).getSection();
280                                    seqNumberfound = iSectionsInSeq; // save
281                                                                     // for
282                                                                     // later
283                                    // debug code
284                                    log.trace("SectionName[{}] getState[{}] occupancy[{}] ",
285                                            sS.getDisplayName(USERSYS),
286                                            sS.getState(), sS.getOccupancy());
287                                    if (!checkUnallocatedCleanly(activeTrain, sS)) {
288                                        areForwardsFree = false;
289                                    } else if (sS.getState() != Section.FREE) {
290                                        log.debug("{}: Forward section [{}] unavailable", trainName,
291                                                sS.getDisplayName(USERSYS));
292                                        areForwardsFree = false;
293                                    } else if (sS.getOccupancy() != Section.UNOCCUPIED) {
294                                        log.debug("{}: Forward section [{}] is not unoccupied", trainName,
295                                                sS.getDisplayName(USERSYS));
296                                        areForwardsFree = false;
297                                    } else if (_dispatcher.checkBlocksNotInAllocatedSection(sS, ar) != null) {
298                                        log.debug("{}: Forward section [{}] is in conflict with [{}]",
299                                                trainName, sS.getUserName(),
300                                                _dispatcher.checkBlocksNotInAllocatedSection(sS, ar));
301                                        areForwardsFree = false;
302                                    } else if (checkBlocksNotInReservedSection(activeTrain, sS) != null) {
303                                        log.debug("{}: Forward section [{}] is in conflict with [{}]",
304                                                trainName, sS.getDisplayName(),
305                                                checkBlocksNotInReservedSection(activeTrain, sS).getDisplayName());
306                                        areForwardsFree = false;
307
308                                    } else if (reservedSections.get(sS.getSystemName()) != null &&
309                                            !reservedSections.get(sS.getSystemName()).equals(trainName)) {
310                                        log.debug("{}: Forward section [{}] is reserved for [{}]",
311                                                trainName, sS.getDisplayName(USERSYS),
312                                                reservedSections.get(sS.getSystemName()));
313                                        areForwardsFree = false;
314                                    } else {
315                                        log.debug("Adding [{}],[{}]", sS.getDisplayName(USERSYS), trainName);
316                                        reservedSections.put(sS.getSystemName(), trainName);
317                                        areForwardsFree = true;
318                                    }
319                                }
320                                if (!areForwardsFree) {
321                                    // delete all reserves for this train
322                                    removeAllReservesForTrain(trainName);
323                                    break;
324                                }
325                                if (sectionsInSeq.get(seqNumberfound).isSafe()) {
326                                    log.trace("Safe Section Found");
327                                    break;
328                                }
329                            }
330
331                            log.trace("ForwardsFree[{}]", areForwardsFree);
332                            if (!areForwardsFree) {
333                                // delete all reserves for this train
334                                removeAllReservesForTrain(trainName);
335                                continue;
336                            }
337                        }
338                        String sectionSystemName;
339                        try {
340                            sectionSystemName = ar.getSection().getSystemName();
341                        } catch (Exception ex) {
342                            log.error("Error", ex);
343                            sectionSystemName = "Unknown";
344                        }
345                        if (allocateMore(ar)) {
346                            // First Time thru this will in the list
347                            if (!sectionSystemName.equals("Unknown")) {
348                                log.debug("removing : [{}]", sectionSystemName);
349                                reservedSections.remove(sectionSystemName);
350                            } else {
351                                log.error("{};Cannot allocate allocatable section[{}]", trainName,
352                                        sectionSystemName);
353                            }
354                        }
355                        continue;
356                    } // end of allocating by safe sections
357                }
358                log.trace("Using Regular");
359                if (!checkUnallocatedCleanly(activeTrain, ar.getSection())) {
360                    okToAllocate = false;
361                    continue;
362                }
363                if (getPlanThisTrain(activeTrain) != null) {
364                    // this train is in an active Allocation Plan, anything
365                    // to do now?
366                    if (willAllocatingFollowPlan(ar, getPlanThisTrain(activeTrain))) {
367                        if (allocateMore(ar)) {
368                            continue;
369                        }
370                    }
371                } else if (!waitingForStartTime(ar)) {
372                    // train isn't waiting, continue only if requested
373                    // Section is currently free and not occupied
374                    if ((ar.getSection().getState() == Section.FREE) &&
375                            (ar.getSection().getOccupancy() != Section.OCCUPIED) &&
376                            (_dispatcher.getSignalType() == DispatcherFrame.SIGNALHEAD ||
377                                    _dispatcher.getSignalType() == DispatcherFrame.SECTIONSALLOCATED ||
378                                    (_dispatcher.getSignalType() == DispatcherFrame.SIGNALMAST &&
379                                            _dispatcher.checkBlocksNotInAllocatedSection(ar.getSection(),
380                                                    ar) == null))) {
381                        // requested Section is currently free and not
382                        // occupied
383                        List<ActiveTrain> activeTrainsList = _dispatcher.getActiveTrainsList();
384                        if (activeTrainsList.size() == 1) {
385                            // this is the only ActiveTrain
386                            if (allocateMore(ar)) {
387                                continue;
388                            }
389                        } else {
390                            // check if any other ActiveTrain will need this
391                            // Section or its alternates, if any
392                            okToAllocate = true;
393                            List<ActiveTrain> neededByTrainList = new ArrayList<>();
394                            for (int j = 0; j < activeTrainsList.size(); j++) {
395                                ActiveTrain at = activeTrainsList.get(j);
396                                if (at != activeTrain) {
397                                    if (sectionNeeded(ar, at)) {
398                                        neededByTrainList.add(at);
399                                    }
400                                }
401                            }
402                            // requested Section (or alternate) is
403                            // needed by other active Active Train(s)
404                            for (int k = 0; k < neededByTrainList.size(); k++) {
405                                // section is also needed by this active
406                                // train
407                                ActiveTrain nt = neededByTrainList.get(k);
408                                // are trains moving in same direction
409                                // through the requested Section?
410                                if (sameDirection(ar, nt)) {
411                                    // trains will move in the same
412                                    // direction thru requested section
413                                    if (firstTrainLeadsSecond(activeTrain, nt) &&
414                                            (nt.getPriority() > activeTrain.getPriority())) {
415                                        // a higher priority train is
416                                        // trailing this train, can we
417                                        // let it pass?
418                                        if (checkForPassingPlan(ar, nt, neededByTrainList)) {
419                                            // PASSING_MEET plan created
420                                            if (!willAllocatingFollowPlan(ar,
421                                                    getPlanThisTrain(activeTrain))) {
422                                                okToAllocate = false;
423                                            }
424                                        }
425                                    }
426                                } else {
427                                    // trains will move in opposite
428                                    // directions thru requested section
429                                    // explore possibility of an
430                                    // XING_MEET to avoid gridlock
431                                    if (willTrainsCross(activeTrain, nt)) {
432                                        if (checkForXingPlan(ar, nt, neededByTrainList)) {
433                                            // XING_MEET plan created
434                                            if (!willAllocatingFollowPlan(ar,
435                                                    getPlanThisTrain(activeTrain))) {
436                                                okToAllocate = false;
437                                            }
438                                        }
439                                    }
440                                }
441                            }
442                            if (okToAllocate) {
443                                if (allocateMore(ar)) {
444                                    continue;
445                                }
446                            }
447                        }
448                    }
449                }
450            } catch (RuntimeException e) {
451                log.warn(
452                        "scanAllocationRequestList - maybe the allocationrequest was removed due to a terminating train??",e);
453                continue;
454            }
455        }
456    }
457
458    /**
459     * Remove all reserved sections for a train name
460     *
461     * @param trainName remove reserved spaces for this train
462     */
463    protected void removeAllReservesForTrain(String trainName) {
464        Iterator<Entry<String, String>> iterRS = reservedSections.entrySet().iterator();
465        while (iterRS.hasNext()) {
466            Map.Entry<String, String> pair = iterRS.next();
467            if (pair.getValue().equals(trainName)) {
468                iterRS.remove();
469            }
470        }
471    }
472
473    /**
474     * Remove a specific section reservation for a train.
475     *
476     * @param trainName         Name of the train
477     * @param sectionSystemName Systemname
478     */
479    protected void releaseReservation(String trainName, String sectionSystemName) {
480        String reservedTrainName = reservedSections.get(sectionSystemName);
481        if (reservedTrainName.equals(trainName)) {
482            reservedSections.remove(sectionSystemName);
483        }
484    }
485
486    /*
487     * Check conflicting blocks acros reserved sections.
488     */
489    protected Section checkBlocksNotInReservedSection(ActiveTrain at, Section sectionToCheck) {
490        String trainName = at.getTrainName();
491        List<Block> lb = sectionToCheck.getBlockList();
492        Iterator<Entry<String, String>> iterRS = reservedSections.entrySet().iterator();
493        while (iterRS.hasNext()) {
494            Map.Entry<String, String> pair = iterRS.next();
495            if (!pair.getValue().equals(trainName)) {
496                Section s = InstanceManager.getDefault(jmri.SectionManager.class).getSection(pair.getKey());
497                for (Block rb : s.getBlockList()) {
498                    if (lb.contains(rb)) {
499                        return s;
500                    }
501                }
502            }
503        }
504        return null;
505    }
506
507    /*
508     * Check each ActiveTrains sections for a given section. We need to do this
509     * as Section is flagged as free before it is fully released, then when it
510     * is released it updates , incorrectly, the section status and allocations.
511     */
512    private boolean checkUnallocatedCleanly(ActiveTrain at, Section section) {
513        for (ActiveTrain atx : InstanceManager.getDefault(DispatcherFrame.class).getActiveTrainsList()) {
514            for (AllocatedSection asx : atx.getAllocatedSectionList()) {
515                if (asx.getSection() == section) {
516                    return false;
517                }
518            }
519        }
520        return true;
521    }
522
523    /**
524     * Entered to request a choice of Next Section when a Section is being
525     * allocated and there are alternate Section choices for the next Section.
526     *
527     * @param sList the possible next Sections
528     * @param ar    the section being allocated when a choice is needed
529     * @param sectionSeqNo transit sequence number attempting to be allocated
530     * @return the allocated section
531     */
532    protected Section autoNextSectionChoice(List<Section> sList, AllocationRequest ar, int sectionSeqNo) {
533        // check if AutoAllocate has prepared for this question
534        if ((savedAR != null) && (savedAR == ar)) {
535            for (int j = 0; j < sList.size(); j++) {
536                if (savedSection == sList.get(j)) {
537                    return savedSection;
538                }
539            }
540            log.warn("Failure of prepared choice of next Section in AutoAllocate");
541        }
542
543        // check to see if AutoAllocate by safesections has reserved a section
544        // already
545        ActiveTrain at = ar.getActiveTrain();
546        for (Section sectionOption : sList) {
547            String reservedTrainName = reservedSections.get(sectionOption.getSystemName());
548            if (reservedTrainName != null) {
549                if (reservedTrainName.equals(at.getTrainName())) {
550                    return sectionOption;
551                }
552            }
553        }
554
555        // Jay Janzen
556        // If there is an AP check to see if the AP's target is on the list of
557        // choices
558        // and if so, return that.
559        at = ar.getActiveTrain();
560        AllocationPlan ap = getPlanThisTrain(at);
561        Section as = null;
562        if (ap != null) {
563            if (ap.getActiveTrain(1) == at) {
564                as = ap.getTargetSection(1);
565            } else if (ap.getActiveTrain(2) == at) {
566                as = ap.getTargetSection(2);
567            } else {
568                return null;
569            }
570            for (int i = 0; i < sList.size(); i++) {
571                if (as != null && as == sList.get(i)) {
572                    return as;
573                }
574            }
575        }
576        // If our end block section is on the list of choices
577        // return that occupied or not. In the list of choices the primary
578        // occurs
579        // ahead any alternates, so if our end block is an alternate and its
580        // primary is unoccupied, the search will select the primary and
581        // we wind up skipping right over our end section.
582        for (int i = 0; i < sList.size(); i++) {
583            if (at.getEndBlockSectionSequenceNumber() == sectionSeqNo
584                     && at.getEndBlockSection().getSystemName().equals(sList.get(i).getSystemName())) {
585                return sList.get(i);
586            }
587        }
588        // no prepared choice, or prepared choice failed, is there an unoccupied
589        // Section available
590        for (int i = 0; i < sList.size(); i++) {
591            if ((sList.get(i).getOccupancy() == Section.UNOCCUPIED) &&
592                    (sList.get(i).getState() == Section.FREE) &&
593                    (_dispatcher.getSignalType() == DispatcherFrame.SIGNALHEAD ||
594                            _dispatcher.getSignalType() == DispatcherFrame.SECTIONSALLOCATED ||
595                            (_dispatcher.getSignalType() == DispatcherFrame.SIGNALMAST &&
596                                    _dispatcher.checkBlocksNotInAllocatedSection(sList.get(i), ar) == null))) {
597                return sList.get(i);
598            }
599        }
600        // no unoccupied Section available, check for Section allocated in same
601        // direction as this request
602        int dir = ar.getSectionDirection();
603        List<AllocatedSection> allocatedSections = _dispatcher.getAllocatedSectionsList();
604        for (int m = 0; m < sList.size(); m++) {
605            boolean notFound = true;
606            for (int k = 0; (k < allocatedSections.size()) && notFound; k++) {
607                if (sList.get(m) == allocatedSections.get(k).getSection()) {
608                    notFound = false;
609                    if (allocatedSections.get(k).getSection().getState() == dir) {
610                        return sList.get(m);
611                    }
612                }
613            }
614        }
615        // if all else fails, return null so Dispatcher will ask the dispatcher
616        // to choose
617        return null;
618    }
619
620    private final AllocationRequest savedAR = null;
621    private final Section savedSection = null;
622
623    // private implementation methods
624    private void copyAndSortARs(List<AllocationRequest> list) {
625        orderedRequests.clear();
626        // copy across and then sort...
627        for (int i = 0; i < list.size(); i++) {
628            orderedRequests.add(list.get(i));
629        }
630        orderedRequests.sort((AllocationRequest e1, AllocationRequest e2) -> {
631            if (e1.getActiveTrain().getPriority() < e2.getActiveTrain().getPriority()) {
632                return 1;
633            } else if (e1.getActiveTrain().getPriority() > e2.getActiveTrain().getPriority()) {
634                return -1;
635            } else {
636                return e1.getActiveTrain().getTrainName().compareTo(e2.getActiveTrain().getTrainName());
637            }
638        });
639    }
640
641    /**
642     * Check whether it is nessassary to pause/stop further allocation because a
643     * specified sensor is active.
644     *
645     * @param lastAllocatedTransitSection
646     * @return true stop allocating, false dont
647     */
648    private boolean stopAllocateSensorSet(ActiveTrain at, TransitSection lastAllocatedTransitSection) {
649        if (lastAllocatedTransitSection.getStopAllocatingSensor() != null &&
650                !lastAllocatedTransitSection.getStopAllocatingSensor().equals("")) {
651            String sensorName = lastAllocatedTransitSection.getStopAllocatingSensor();
652            Sensor sensor;
653            try {
654                sensor = InstanceManager.sensorManagerInstance().provideSensor(sensorName);
655                if (sensor.getKnownState() == Sensor.ACTIVE) {
656                    log.trace("Sensor[{}] InActive", sensor.getDisplayName(USERSYS));
657                    at.initializeRestartAllocationSensor(jmri.InstanceManager
658                            .getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(sensorName, sensor));
659                    return true;
660                }
661            } catch (NumberFormatException ex) {
662                log.error("Error with pause/stop allocation sensor[{}]", sensorName, ex);
663                return false;
664            }
665        }
666        return false;
667    }
668
669    private AllocationPlan getPlanThisTrain(ActiveTrain at) {
670        for (int i = 0; i < _planList.size(); i++) {
671            AllocationPlan ap = _planList.get(i);
672            for (int j = 1; j < 3; j++) {
673                if (ap.getActiveTrain(j) == at) {
674                    return ap;
675                }
676            }
677        }
678        // train not in an AllocationPlan
679        return null;
680    }
681
682    private boolean willAllocatingFollowPlan(AllocationRequest ar, AllocationPlan ap) {
683        // return 'true' if this AllocationRequest is consistent with specified
684        // plan,
685        // returns 'false' otherwise
686        ActiveTrain at = ar.getActiveTrain();
687        int cTrainNum = 0;
688        if (ap.getActiveTrain(1) == at) {
689            cTrainNum = 1;
690        } else if (ap.getActiveTrain(2) == at) {
691            cTrainNum = 2;
692        } else {
693            log.error("Requesting train not in Allocation Plan");
694            return false;
695        }
696        if (!at.isAllocationReversed()) {
697            if (ap.getTargetSectionSequenceNum(cTrainNum) >= ar.getSectionSeqNumber()) {
698                if ((ar.getSection().getState() == Section.FREE) &&
699                        (ar.getSection().getOccupancy() != Section.OCCUPIED)) {
700                    return true;
701                }
702            }
703        } else {
704            if (ap.getTargetSectionSequenceNum(cTrainNum) <= ar.getSectionSeqNumber()) {
705                if ((ar.getSection().getState() == Section.FREE) &&
706                        (ar.getSection().getOccupancy() != Section.OCCUPIED)) {
707                    return true;
708                }
709            }
710        }
711        return false;
712    }
713
714    private void removeCompletePlans() {
715        boolean foundCompletePlan = true;
716        while (foundCompletePlan) {
717            foundCompletePlan = false;
718            for (int i = 0; (!foundCompletePlan) && (i < _planList.size()); i++) {
719                // remove if all planned allocations have been made
720                foundCompletePlan = _planList.get(i).isComplete();
721                if (foundCompletePlan) {
722                    _planList.get(i).dispose();
723                    _planList.remove(i);
724                }
725            }
726        }
727    }
728
729    protected void clearAllocationPlans() {
730        for (int i = _planList.size() - 1; i >= 0; i--) {
731            AllocationPlan ap = _planList.get(i);
732            _planList.remove(i);
733            ap.dispose();
734        }
735    }
736
737    // test to see how far ahead allocations have already been made
738    // and go no farther than the number requested, or the next safe section.
739    // return true if allocation successful, false otherwise
740    private boolean allocateMore(AllocationRequest ar) {
741        log.trace("in allocateMore, ar.Section={}", ar.getSection().getDisplayName(USERSYS));
742        int allocateSectionsAhead = ar.getActiveTrain().getAllocateMethod();
743        if (allocateSectionsAhead == ActiveTrain.ALLOCATE_AS_FAR_AS_IT_CAN) {
744            if (_dispatcher.allocateSection(ar, null) == null) {
745                return false;
746            }
747            return true;
748        }
749        // test how far ahead of occupied track this requested section is
750        List<AllocatedSection> aSectionList = ar.getActiveTrain().getAllocatedSectionList();
751        boolean allocateBySafeSections = false;
752        // check for allocating By Safe Sections
753        if (allocateSectionsAhead == 0) {
754            // check for type of allocating N ahead or until passing
755            allocateBySafeSections = true;
756        }
757        if ((allocateBySafeSections && aSectionList.size() >= 1) ||
758                (!allocateBySafeSections && aSectionList.size() >= (allocateSectionsAhead + 1))) {
759            int curSeq = ar.getSectionSeqNumber() - 1;
760            if (ar.getActiveTrain().isAllocationReversed()) {
761                curSeq = ar.getSectionSeqNumber() + 1;
762            }
763            if ((curSeq == 1) && ar.getActiveTrain().getResetWhenDone()) {
764                curSeq = ar.getActiveTrain().getTransit().getMaxSequence();
765            }
766            AllocatedSection curAS = null;
767            for (int i = aSectionList.size() - 1; i >= 0; i--) {
768                AllocatedSection as = aSectionList.get(i);
769                if ((as != null) && (as.getSequence() == curSeq)) {
770                    curAS = as;
771                }
772            }
773            if (allocateBySafeSections &&
774                    (curAS != null) &&
775                    ((curAS.getSection().getOccupancy() != jmri.Section.OCCUPIED) &&
776                            (ar.getActiveTrain().getLastAllocOverrideSafe() == null ||
777                                    ( ar.getActiveTrain().getLastAllocOverrideSafe() != curAS.getSection())) &&
778                            ar.getActiveTrain().getTransit()
779                                    .getTransitSectionFromSectionAndSeq(curAS.getSection(), curSeq).isSafe())) {
780                // last allocated section exists and is not occupied but is a
781                // Passing point
782                // block further allocations till occupied.
783                log.trace("{}: not at end of safe allocations, [{}] not allocated", ar.getActiveTrain().getTrainName(),
784                        ar.getSection().getDisplayName(USERSYS));
785                return false;
786            } else if (allocateBySafeSections) {
787                log.trace("auto allocating Section keep going");
788                if (_dispatcher.allocateSection(ar, null) != null) {
789                    return true;
790                } else {
791                    return false;
792                }
793            }
794            log.trace("Auto allocating by count");
795            int numberAllocatedButUnoccupied = 0;
796            for (int i = aSectionList.size() - 1; i >= 0; i--) {
797                AllocatedSection as = aSectionList.get(i);
798                if ((as != null) && (as.getSection().getOccupancy() != jmri.Section.OCCUPIED && !as.getExited())) {
799                    numberAllocatedButUnoccupied++;
800                }
801            }
802            log.trace("FinalCounter[{}]", numberAllocatedButUnoccupied);
803            if (numberAllocatedButUnoccupied < allocateSectionsAhead) {
804                if (_dispatcher.allocateSection(ar, null) == null) {
805                    return false;
806                }
807                return true;
808            }
809            return false;
810
811        }
812        log.debug("{}: auto allocating Section {}", ar.getActiveTrain().getTrainName(),
813                ar.getSection().getDisplayName(USERSYS));
814        if (_dispatcher.allocateSection(ar, null) == null) {
815            return false;
816        }
817        return true;
818    }
819
820    private boolean checkForXingPlan(AllocationRequest ar, ActiveTrain nt,
821            List<ActiveTrain> neededByTrainList) {
822        // returns 'true' if an AllocationPlan has been set up, returns 'false'
823        // otherwise
824        Section nSec = null;
825        Section aSec = null;
826        int nSecSeq = 0;
827        int aSecSeq = 0;
828        ActiveTrain at = ar.getActiveTrain();
829        AllocationPlan apx = getPlanThisTrain(nt);
830        if (apx != null) {
831            if (apx.getPlanType() != AllocationPlan.XING_MEET) {
832                return false;
833            }
834            // already in a XING_MEET Allocation Plan - find target Section and
835            // sequence
836            if (apx.getActiveTrain(1) == nt) {
837                nSecSeq = apx.getTargetSectionSequenceNum(1);
838                nSec = apx.getTargetSection(1);
839            } else {
840                nSecSeq = apx.getTargetSectionSequenceNum(2);
841                nSec = apx.getTargetSection(2);
842            }
843            List<Section> nSections = nt.getTransit().getSectionListBySeq(nSecSeq);
844            if (nSections.size() <= 1) {
845                return false;
846            }
847            // is a passing siding, find a suitable track
848            aSec = getBestOtherSection(nSections, nSec);
849            if (aSec == null) {
850                return false;
851            }
852            aSecSeq = willTraverse(aSec, at, getCurrentSequenceNumber(at));
853            if (aSecSeq == 0) {
854                return false;
855            }
856        } else {
857            // neither train is in an AllocationPlan currently, check for
858            // suitable passing siding
859            int aSeq = ar.getSectionSeqNumber();
860            // is an alternate Section available here or ahead
861            aSecSeq = findPassingSection(at, aSeq);
862            if (aSecSeq == 0) {
863                // none in at's Transit, is there one in nt's Transit
864                int nCurrentSeq = getCurrentSequenceNumber(nt);
865                nSecSeq = findPassingSection(nt, nCurrentSeq);
866                if (nSecSeq > 0) {
867                    // has passing section ahead, will this train traverse a
868                    // Section in it
869                    List<Section> nSections = nt.getTransit().getSectionListBySeq(nSecSeq);
870                    for (int i = 0; (i < nSections.size()) && (aSec == null); i++) {
871                        aSecSeq = willTraverse(nSections.get(i), at, aSeq);
872                        if (aSecSeq > 0) {
873                            aSec = at.getTransit().getSectionListBySeq(aSecSeq).get(0);
874                        }
875                    }
876                    if (aSec != null) {
877                        // found passing Section that should work out
878                        nSec = getBestOtherSection(nSections, aSec);
879                    }
880                }
881            } else {
882                // will other train go through any of these alternate sections
883                List<Section> aSections = at.getTransit().getSectionListBySeq(aSecSeq);
884                int nCurrentSeq = getCurrentSequenceNumber(nt);
885                for (int i = 0; (i < aSections.size()) && (aSec == null); i++) {
886                    nSecSeq = willTraverse(aSections.get(i), nt, nCurrentSeq);
887                    if (nSecSeq > 0) {
888                        nSec = aSections.get(i);
889                        aSec = getBestOtherSection(aSections, nSec);
890                    }
891                }
892            }
893            // if could not find a suitable siding for a crossing meet, return
894            if ((aSec == null) || (nSec == null)) {
895                return false;
896            }
897        }
898        // check for conflicting train or conflicting plan that could cause
899        // gridlock
900        if (neededByTrainList.size() > 2) {
901            // is there another train between these two
902            if (!areTrainsAdjacent(at, nt)) {
903                return false;
904            }
905            if (isThereConflictingPlan(at, aSec, aSecSeq, nt, nSec, nSecSeq,
906                    AllocationPlan.XING_MEET)) {
907                return false;
908            }
909        }
910        // set up allocation plan
911        AllocationPlan ap = new AllocationPlan(this, nextPlanNum);
912        nextPlanNum++;
913        ap.setPlanType(AllocationPlan.XING_MEET);
914        ap.setActiveTrain(at, 1);
915        ap.setTargetSection(aSec, aSecSeq, 1);
916        ap.setActiveTrain(nt, 2);
917        ap.setTargetSection(nSec, nSecSeq, 2);
918        _planList.add(ap);
919        return true;
920    }
921
922    private boolean checkForPassingPlan(AllocationRequest ar, ActiveTrain nt,
923            List<ActiveTrain> neededByTrainList) {
924        // returns 'true' if an AllocationPlan has been set up, returns 'false'
925        // otherwise
926        Section nSec = null;
927        Section aSec = null;
928        int nSecSeq = 0;
929        int aSecSeq = 0;
930        ActiveTrain at = ar.getActiveTrain();
931        AllocationPlan apx = getPlanThisTrain(nt);
932        if (apx != null) {
933            if (apx.getPlanType() != AllocationPlan.PASSING_MEET) {
934                return false;
935            }
936            // already in a PASSING_MEET Allocation Plan - find target Section
937            // and sequence
938            Section oSection = null;
939            // ActiveTrain oTrain = null;
940            if (apx.getActiveTrain(1) == nt) {
941                nSecSeq = apx.getTargetSectionSequenceNum(1);
942                nSec = apx.getTargetSection(1);
943                oSection = apx.getTargetSection(2);
944            } else {
945                nSecSeq = apx.getTargetSectionSequenceNum(2);
946                nSec = apx.getTargetSection(2);
947                oSection = apx.getTargetSection(1);
948            }
949            int aCurrentSeq = getCurrentSequenceNumber(at);
950            aSecSeq = willTraverse(nSec, at, aCurrentSeq);
951            if (aSecSeq == 0) {
952                return false;
953            }
954            List<Section> nSections = nt.getTransit().getSectionListBySeq(nSecSeq);
955            if (nSections.size() <= 1) {
956                return false;
957            }
958            // is a passing siding, find a suitable track
959            for (int i = 0; (i < nSections.size()) && (aSec == null); i++) {
960                if (nSections.get(i) == oSection) {
961                    aSecSeq = willTraverse(nSections.get(i), at, aCurrentSeq);
962                    if (aSecSeq > 0) {
963                        aSec = nSections.get(i);
964                    }
965                }
966            }
967            if (aSec == null) {
968                for (int i = 0; (i < nSections.size()) && (aSec == null); i++) {
969                    if (nSections.get(i) != nSec) {
970                        aSecSeq = willTraverse(nSections.get(i), at, aCurrentSeq);
971                        if (aSecSeq > 0) {
972                            aSec = nSections.get(i);
973                        }
974                    }
975                }
976            }
977            if (aSec == null) {
978                return false;
979            }
980        } else {
981            // both trains are not in Allocation plans
982            int aSeq = ar.getSectionSeqNumber();
983            // is an alternate Section available here or ahead
984            aSecSeq = findPassingSection(at, aSeq);
985            if (aSecSeq == 0) {
986                // does higher priority train have a passing section ahead
987                int nCurrentSeq = getCurrentSequenceNumber(nt);
988                nSecSeq = findPassingSection(nt, nCurrentSeq);
989                if (nSecSeq > 0) {
990                    // has passing section ahead, will this train traverse a
991                    // Section in it
992                    List<Section> nSections = nt.getTransit().getSectionListBySeq(nSecSeq);
993                    for (int i = 0; (i < nSections.size()) && (aSec == null); i++) {
994                        aSecSeq = willTraverse(nSections.get(i), at, aSeq);
995                        if (aSecSeq > 0) {
996                            aSec = at.getTransit().getSectionListBySeq(aSecSeq).get(0);
997                        }
998                    }
999                    if (aSec != null) {
1000                        // found passing Section that should work out
1001                        nSec = getBestOtherSection(nSections, aSec);
1002                    }
1003                }
1004            } else {
1005                // will the higher priority train go through any of these
1006                // alternate sections
1007                List<Section> aSections = at.getTransit().getSectionListBySeq(aSecSeq);
1008                int nCurrentSeq = getCurrentSequenceNumber(nt);
1009                for (int i = 0; (i < aSections.size()) && (aSec == null); i++) {
1010                    nSecSeq = willTraverse(aSections.get(i), nt, nCurrentSeq);
1011                    if (nSecSeq > 0) {
1012                        nSec = aSections.get(i);
1013                        aSec = getBestOtherSection(aSections, nSec);
1014                    }
1015                }
1016            }
1017            // if could not find a suitable passing siding, return
1018            if ((aSec == null) || (nSec == null)) {
1019                return false;
1020            }
1021            // push higher priority train one section further, if possible
1022            if (!nt.isAllocationReversed()) {
1023                if (nSecSeq < nt.getTransit().getMaxSequence()) {
1024                    nSecSeq++;
1025                    nSec = nt.getTransit().getSectionListBySeq(nSecSeq).get(0);
1026                }
1027            } else {
1028                if (nSecSeq > 1) {
1029                    nSecSeq--;
1030                    nSec = nt.getTransit().getSectionListBySeq(nSecSeq).get(0);
1031                }
1032            }
1033        }
1034        // is there another train trying to let this high priority train pass
1035        if (neededByTrainList.size() > 2) {
1036            // Note: e.g. Two lower priority trains ahead of a high priority
1037            // train could cause gridlock
1038            // if both try to set up a PASSING_PLAN meet at the same place, so
1039            // we exclude that case.
1040            // is there another train between these two
1041            if (!areTrainsAdjacent(at, nt)) {
1042                return false;
1043            }
1044            if (isThereConflictingPlan(at, aSec, aSecSeq, nt, nSec, nSecSeq,
1045                    AllocationPlan.PASSING_MEET)) {
1046                return false;
1047            }
1048        }
1049        // set up allocation plan
1050        AllocationPlan ap = new AllocationPlan(this, nextPlanNum);
1051        nextPlanNum++;
1052        ap.setPlanType(AllocationPlan.PASSING_MEET);
1053        ap.setActiveTrain(at, 1);
1054        ap.setTargetSection(aSec, aSecSeq, 1);
1055        ap.setActiveTrain(nt, 2);
1056        ap.setTargetSection(nSec, nSecSeq, 2);
1057        _planList.add(ap);
1058        return true;
1059    }
1060
1061    private boolean isThereConflictingPlan(ActiveTrain at, Section aSec, int aSecSeq,
1062            ActiveTrain nt, Section nSec, int nSecSeq, int type) {
1063        // returns 'true' if there is a conflicting plan that may result in
1064        // gridlock
1065        // if this plan is set up, return 'false' if not.
1066        // Note: may have to add other tests to this method in the future to
1067        // prevent gridlock
1068        // situations not currently tested for.
1069        if (_planList.size() == 0) {
1070            return false;
1071        }
1072        for (int i = 0; i < _planList.size(); i++) {
1073            AllocationPlan ap = _planList.get(i);
1074            // check if this plan involves the second train (it'll never involve
1075            // the first)
1076            int trainNum = 0;
1077            if (ap.getActiveTrain(1) == nt) {
1078                trainNum = 1;
1079            } else if (ap.getActiveTrain(2) == nt) {
1080                trainNum = 2;
1081            }
1082            if (trainNum > 0) {
1083                // check consistency - same type, section, and sequence number
1084                if ((ap.getPlanType() != type) ||
1085                        (ap.getTargetSection(trainNum) != nSec) ||
1086                        (ap.getTargetSectionSequenceNum(trainNum) != nSecSeq)) {
1087                    return true;
1088                }
1089            } else {
1090                // different trains, does this plan use the same Passing
1091                // Section?
1092                List<Section> aSections = at.getTransit().getSectionListBySeq(aSecSeq);
1093                for (int j = 0; j < aSections.size(); j++) {
1094                    if ((aSections.get(j) == ap.getTargetSection(1)) || (aSections.get(j) == ap.getTargetSection(2))) {
1095                        return true;
1096                    }
1097                }
1098            }
1099        }
1100        // passes all tests
1101        return false;
1102    }
1103
1104    private Section getBestOtherSection(List<Section> sList, Section aSec) {
1105        // returns the best Section from the list that is not aSec, or else
1106        // return null
1107        for (int i = 0; i < sList.size(); i++) {
1108            if ((sList.get(i) != aSec) &&
1109                    (sList.get(i).getState() == Section.FREE) &&
1110                    (sList.get(i).getOccupancy() != Section.OCCUPIED)) {
1111                return sList.get(i);
1112            }
1113        }
1114        for (int i = 0; i < sList.size(); i++) {
1115            if ((sList.get(i) != aSec) && (sList.get(i).getOccupancy() != Section.OCCUPIED)) {
1116                return sList.get(i);
1117            }
1118        }
1119        for (int i = 0; i < sList.size(); i++) {
1120            if (sList.get(i) != aSec) {
1121                return sList.get(i);
1122            }
1123        }
1124        return null;
1125    }
1126
1127    private int findPassingSection(ActiveTrain at, int aSeq) {
1128        // returns the sequence number of first area having alternate sections
1129        Transit t = at.getTransit();
1130        if (!at.isTransitReversed()) {
1131            for (int i = aSeq; i <= t.getMaxSequence(); i++) {
1132                if (t.getSectionListBySeq(i).size() > 1) {
1133                    return i;
1134                }
1135            }
1136        } else {
1137            for (int i = aSeq; i >= 0; i--) {
1138                if (t.getSectionListBySeq(i).size() > 1) {
1139                    return i;
1140                }
1141            }
1142        }
1143        return 0;
1144    }
1145
1146    private int willTraverse(Section s, ActiveTrain at, int seq) {
1147        Transit t = at.getTransit();
1148        if (!at.isTransitReversed()) {
1149            for (int i = seq; i <= t.getMaxSequence(); i++) {
1150                for (int j = 0; j < t.getSectionListBySeq(i).size(); j++) {
1151                    if (t.getSectionListBySeq(i).get(j) == s) {
1152                        return i;
1153                    }
1154                }
1155            }
1156        } else {
1157            for (int i = seq; i >= 0; i--) {
1158                for (int j = 0; j < t.getSectionListBySeq(i).size(); j++) {
1159                    if (t.getSectionListBySeq(i).get(j) == s) {
1160                        return i;
1161                    }
1162                }
1163            }
1164        }
1165        return 0;
1166    }
1167
1168    private boolean sectionNeeded(AllocationRequest ar, ActiveTrain at) {
1169        // returns 'true' if request section, or its alternates, will be needed
1170        // by specified train
1171        if ((ar == null) || (at == null)) {
1172            log.error("null argument on entry to 'sectionNeeded'");
1173            return false;
1174        }
1175        List<Section> aSectionList = ar.getActiveTrain().getTransit().getSectionListBySeq(
1176                ar.getSectionSeqNumber());
1177        boolean found = false;
1178        for (int i = 0; i < aSectionList.size(); i++) {
1179            if (!(at.getTransit().containsSection(aSectionList.get(i)))) {
1180                found = true;
1181            }
1182        }
1183        if (!found) {
1184            return false;
1185        } else if ((at.getResetWhenDone()) || (at.getReverseAtEnd() && (!at.isAllocationReversed()))) {
1186            return true;
1187        }
1188        // this train may need this Section, has it already passed this Section?
1189        List<TransitSection> tsList = at.getTransit().getTransitSectionList();
1190        int curSeq = getCurrentSequenceNumber(at);
1191        if (!at.isAllocationReversed()) {
1192            for (int i = 0; i < tsList.size(); i++) {
1193                if (tsList.get(i).getSequenceNumber() > curSeq) {
1194                    for (int j = 0; j < aSectionList.size(); j++) {
1195                        if (tsList.get(i).getSection() == aSectionList.get(j)) {
1196                            return true;
1197                        }
1198                    }
1199                }
1200            }
1201        } else {
1202            for (int i = tsList.size() - 1; i >= 0; i--) {
1203                if (tsList.get(i).getSequenceNumber() < curSeq) {
1204                    for (int j = 0; j < aSectionList.size(); j++) {
1205                        if (tsList.get(i).getSection() == aSectionList.get(j)) {
1206                            return true;
1207                        }
1208                    }
1209                }
1210            }
1211        }
1212        if (InstanceManager.getDefault(DispatcherFrame.class).getSignalType() == DispatcherFrame.SIGNALMAST) {
1213            if (!at.isAllocationReversed()) {
1214                for (int i = 0; i < tsList.size(); i++) {
1215                    if (tsList.get(i).getSequenceNumber() > curSeq) {
1216                        for (int j = 0; j < aSectionList.size(); j++) {
1217                            if (tsList.get(i).getSection() == aSectionList.get(j)) {
1218                                return true;
1219                            }
1220                        }
1221                    }
1222                }
1223            } else {
1224                for (int i = tsList.size() - 1; i >= 0; i--) {
1225                    if (tsList.get(i).getSequenceNumber() < curSeq) {
1226                        for (int j = 0; j < aSectionList.size(); j++) {
1227                            if (tsList.get(i).getSection() == aSectionList.get(j)) {
1228                                return true;
1229                            }
1230                        }
1231                    }
1232                }
1233            }
1234        }
1235        return false;
1236    }
1237
1238    private boolean sameDirection(AllocationRequest ar, ActiveTrain at) {
1239        // returns 'true' if both trains will move thru the requested section in
1240        // the same direction
1241        if ((ar == null) || (at == null)) {
1242            log.error("null argument on entry to 'sameDirection'");
1243            return false;
1244        }
1245        List<TransitSection> tsList = at.getTransit().getTransitSectionList();
1246        List<TransitSection> rtsList = ar.getActiveTrain().getTransit().getTransitSectionListBySeq(
1247                ar.getSectionSeqNumber());
1248        int curSeq = getCurrentSequenceNumber(at);
1249        if (!at.isAllocationReversed()) {
1250            for (int i = 0; i < tsList.size(); i++) {
1251                if (tsList.get(i).getSequenceNumber() > curSeq) {
1252                    for (int k = 0; k < rtsList.size(); k++) {
1253                        if ((tsList.get(i).getSection() == rtsList.get(k).getSection()) &&
1254                                (tsList.get(i).getDirection() == rtsList.get(k).getDirection())) {
1255                            return true;
1256                        }
1257                    }
1258                }
1259            }
1260        } else {
1261            for (int i = tsList.size() - 1; i >= 0; i--) {
1262                if (tsList.get(i).getSequenceNumber() < curSeq) {
1263                    for (int k = 0; k < rtsList.size(); k++) {
1264                        if ((tsList.get(i).getSection() == rtsList.get(k).getSection()) &&
1265                                (tsList.get(i).getDirection() == rtsList.get(k).getDirection())) {
1266                            return true;
1267                        }
1268                    }
1269                }
1270            }
1271        }
1272        return false;
1273    }
1274
1275    private boolean firstTrainLeadsSecond(ActiveTrain at, ActiveTrain nt) {
1276        int aSeq = getCurrentSequenceNumber(at);
1277        Section aSec = getCurSection();
1278        int nSeq = getCurrentSequenceNumber(nt);
1279        Section nSec = getCurSection();
1280        List<TransitSection> atsList = at.getTransit().getTransitSectionList();
1281        if (!at.isTransitReversed()) {
1282            for (int i = 0; i < atsList.size(); i++) {
1283                if (atsList.get(i).getSequenceNumber() > aSeq) {
1284                    if (atsList.get(i).getSection() == nSec) {
1285                        // first train has not yet reached second train position
1286                        return false;
1287                    }
1288                }
1289            }
1290        } else {
1291            for (int i = atsList.size() - 1; i <= 0; i--) {
1292                if (atsList.get(i).getSequenceNumber() < aSeq) {
1293                    if (atsList.get(i).getSection() == nSec) {
1294                        // first train has not yet reached second train position
1295                        return false;
1296                    }
1297                }
1298            }
1299        }
1300        List<TransitSection> ntsList = nt.getTransit().getTransitSectionList();
1301        if (!nt.isTransitReversed()) {
1302            for (int i = 0; i < ntsList.size(); i++) {
1303                if (ntsList.get(i).getSequenceNumber() > nSeq) {
1304                    if (ntsList.get(i).getSection() == aSec) {
1305                        // second train has found first train in its on coming
1306                        // Sections
1307                        return true;
1308                    }
1309                }
1310            }
1311        } else {
1312            for (int i = ntsList.size() - 1; i <= 0; i--) {
1313                if (ntsList.get(i).getSequenceNumber() < nSeq) {
1314                    if (ntsList.get(i).getSection() == aSec) {
1315                        // second train has found first train in its on coming
1316                        // Sections
1317                        return true;
1318                    }
1319                }
1320            }
1321        }
1322        return false;
1323    }
1324
1325    private boolean willTrainsCross(ActiveTrain at, ActiveTrain nt) {
1326        // returns true if both trains will eventually reach the others position
1327        int aSeq = getCurrentSequenceNumber(at);
1328        Section aSec = getCurSection();
1329        int nSeq = getCurrentSequenceNumber(nt);
1330        Section nSec = getCurSection();
1331        List<TransitSection> atsList = at.getTransit().getTransitSectionList();
1332        boolean found = false;
1333        if (!at.isTransitReversed()) {
1334            for (int i = 0; (i < atsList.size()) && (!found); i++) {
1335                if (atsList.get(i).getSequenceNumber() > aSeq) {
1336                    if (atsList.get(i).getSection() == nSec) {
1337                        // first train has reached second train position
1338                        found = true;
1339                    }
1340                }
1341            }
1342        } else {
1343            for (int i = atsList.size() - 1; (i <= 0) && (!found); i--) {
1344                if (atsList.get(i).getSequenceNumber() < aSeq) {
1345                    if (atsList.get(i).getSection() == nSec) {
1346                        // first train has reached second train position
1347                        found = true;
1348                    }
1349                }
1350            }
1351        }
1352        if (!found) {
1353            return false;
1354        }
1355        List<TransitSection> ntsList = nt.getTransit().getTransitSectionList();
1356        if (!nt.isTransitReversed()) {
1357            for (int i = 0; i < ntsList.size(); i++) {
1358                if (ntsList.get(i).getSequenceNumber() > nSeq) {
1359                    if (ntsList.get(i).getSection() == aSec) {
1360                        // second train has found first train in its on coming
1361                        // Sections
1362                        return true;
1363                    }
1364                }
1365            }
1366        } else {
1367            for (int i = ntsList.size() - 1; i <= 0; i--) {
1368                if (ntsList.get(i).getSequenceNumber() < nSeq) {
1369                    if (ntsList.get(i).getSection() == aSec) {
1370                        // second train has found first train in its on coming
1371                        // Sections
1372                        return true;
1373                    }
1374                }
1375            }
1376        }
1377        return false;
1378    }
1379
1380    private boolean areTrainsAdjacent(ActiveTrain at, ActiveTrain nt) {
1381        // returns 'false' if a different ActiveTrain has allocated track
1382        // between the
1383        // two trains, returns 'true' otherwise
1384        List<AllocatedSection> allocatedSections = _dispatcher.getAllocatedSectionsList();
1385        List<TransitSection> atsList = at.getTransit().getTransitSectionList();
1386        int aSeq = getCurrentSequenceNumber(at);
1387        Section nSec = getCurSection();
1388        if (willTraverse(nSec, at, aSeq) != 0) {
1389            // at is moving toward nt
1390            if (!at.isTransitReversed()) {
1391                for (int i = 0; i < atsList.size(); i++) {
1392                    if (atsList.get(i).getSequenceNumber() > aSeq) {
1393                        Section tSec = atsList.get(i).getSection();
1394                        if (tSec == nSec) {
1395                            // reached second train position, no train in
1396                            // between
1397                            return true;
1398                        } else {
1399                            for (int j = 0; j < allocatedSections.size(); j++) {
1400                                if (allocatedSections.get(j).getSection() == tSec) {
1401                                    if ((allocatedSections.get(j).getActiveTrain() != at) &&
1402                                            (allocatedSections.get(j).getActiveTrain() != nt)) {
1403                                        // allocated to a third train, trains
1404                                        // not adjacent
1405                                        return false;
1406                                    }
1407                                }
1408                            }
1409                        }
1410                    }
1411                }
1412            } else {
1413                for (int i = atsList.size() - 1; i <= 0; i--) {
1414                    if (atsList.get(i).getSequenceNumber() < aSeq) {
1415                        Section tSec = atsList.get(i).getSection();
1416                        if (tSec == nSec) {
1417                            // reached second train position, no train in
1418                            // between
1419                            return true;
1420                        } else {
1421                            for (int j = 0; j < allocatedSections.size(); j++) {
1422                                if (allocatedSections.get(j).getSection() == tSec) {
1423                                    if ((allocatedSections.get(j).getActiveTrain() != at) &&
1424                                            (allocatedSections.get(j).getActiveTrain() != nt)) {
1425                                        // allocated to a third train, trains
1426                                        // not adjacent
1427                                        return false;
1428                                    }
1429                                }
1430                            }
1431                        }
1432                    }
1433                }
1434            }
1435        } else {
1436            // at is moving away from nt, so backtrack
1437            if (at.isTransitReversed()) {
1438                for (int i = 0; i < atsList.size(); i++) {
1439                    if (atsList.get(i).getSequenceNumber() > aSeq) {
1440                        Section tSec = atsList.get(i).getSection();
1441                        if (tSec == nSec) {
1442                            // reached second train position, no train in
1443                            // between
1444                            return true;
1445                        } else {
1446                            for (int j = 0; j < allocatedSections.size(); j++) {
1447                                if (allocatedSections.get(j).getSection() == tSec) {
1448                                    if ((allocatedSections.get(j).getActiveTrain() != at) &&
1449                                            (allocatedSections.get(j).getActiveTrain() != nt)) {
1450                                        // allocated to a third train, trains
1451                                        // not adjacent
1452                                        return false;
1453                                    }
1454                                }
1455                            }
1456                        }
1457                    }
1458                }
1459            } else {
1460                for (int i = atsList.size() - 1; i <= 0; i--) {
1461                    if (atsList.get(i).getSequenceNumber() < aSeq) {
1462                        Section tSec = atsList.get(i).getSection();
1463                        if (tSec == nSec) {
1464                            // reached second train position, no train in
1465                            // between
1466                            return true;
1467                        } else {
1468                            for (int j = 0; j < allocatedSections.size(); j++) {
1469                                if (allocatedSections.get(j).getSection() == tSec) {
1470                                    if ((allocatedSections.get(j).getActiveTrain() != at) &&
1471                                            (allocatedSections.get(j).getActiveTrain() != nt)) {
1472                                        // allocated to a third train, trains
1473                                        // not adjacent
1474                                        return false;
1475                                    }
1476                                }
1477                            }
1478                        }
1479                    }
1480                }
1481            }
1482        }
1483        return false;
1484    }
1485
1486    private int getCurrentSequenceNumber(ActiveTrain at) {
1487        // finds the current position of the head of the ActiveTrain in its
1488        // Transit
1489        // returns sequence number of current position. getCurSection() returns
1490        // Section.
1491        int seq = 0;
1492        curSection = null;
1493        if (at == null) {
1494            log.error("null argument on entry to 'getCurrentSeqNumber'");
1495            return seq;
1496        }
1497        Section temSection = null;
1498        List<TransitSection> tsList = at.getTransit().getTransitSectionList();
1499        if (!at.isTransitReversed()) {
1500            // find the highest numbered occupied section
1501            for (int i = 0; i < tsList.size(); i++) {
1502                if ((tsList.get(i).getSection().getOccupancy() == Section.OCCUPIED) &&
1503                        isSectionAllocatedToTrain(tsList.get(i).getSection(),
1504                                tsList.get(i).getSequenceNumber(), at)) {
1505                    seq = tsList.get(i).getSequenceNumber();
1506                    temSection = tsList.get(i).getSection();
1507                }
1508            }
1509            if (seq == at.getTransit().getMaxSequence()) {
1510                if (at.getResetWhenDone()) {
1511                    // train may have passed the last Section during continuous
1512                    // running
1513                    boolean further = true;
1514                    for (int j = 0; (j < tsList.size()) && further; j++) {
1515                        if ((tsList.get(j).getSection().getOccupancy() == Section.OCCUPIED) &&
1516                                isSectionAllocatedToTrain(tsList.get(j).getSection(),
1517                                        tsList.get(j).getSequenceNumber(), at)) {
1518                            seq = tsList.get(j).getSequenceNumber();
1519                            temSection = tsList.get(j).getSection();
1520                        } else {
1521                            further = false;
1522                        }
1523                    }
1524                }
1525            }
1526        } else {
1527            // transit is running in reverse
1528            for (int i = tsList.size() - 1; i >= 0; i--) {
1529                if ((tsList.get(i).getSection().getOccupancy() == Section.OCCUPIED) &&
1530                        isSectionAllocatedToTrain(tsList.get(i).getSection(),
1531                                tsList.get(i).getSequenceNumber(), at)) {
1532                    seq = tsList.get(i).getSequenceNumber();
1533                    temSection = tsList.get(i).getSection();
1534                }
1535            }
1536        }
1537        if (seq == 0) {
1538            if (at.getMode() != ActiveTrain.MANUAL) {
1539                log.error("{}: ActiveTrain has no occupied Section. Halting immediately to avoid runaway.",
1540                        at.getTrainName());
1541                at.getAutoActiveTrain().getAutoEngineer().setHalt(true);
1542            } else {
1543                log.debug("{}: ActiveTrain has no occupied Section, running in Manual mode.", at.getTrainName());
1544            }
1545        } else {
1546            curSection = temSection;
1547        }
1548        return seq;
1549    }
1550
1551    Section curSection = null;
1552
1553    // Returns the Section with the sequence number returned by last call to
1554    // getCurrentSequenceNumber
1555    private Section getCurSection() {
1556        return curSection;
1557    }
1558
1559    private boolean isSectionAllocatedToTrain(Section s, int seq, ActiveTrain at) {
1560        if ((s == null) || (at == null)) {
1561            log.error("null argument to isSectionAllocatedToTrain");
1562            return false;
1563        }
1564        List<AllocatedSection> asList = at.getAllocatedSectionList();
1565        for (int i = 0; i < asList.size(); i++) {
1566            if ((asList.get(i).getSection() == s) && asList.get(i).getSequence() == seq) {
1567                return true;
1568            }
1569        }
1570        return false;
1571    }
1572
1573    private boolean waitingForStartTime(AllocationRequest ar) {
1574        if (ar != null) {
1575            ActiveTrain at = ar.getActiveTrain();
1576            if (at == null) {
1577                return false;
1578            }
1579            if ((!at.getStarted()) && at.getDelayedStart() != ActiveTrain.NODELAY || at.reachedRestartPoint()) {
1580                return true;
1581            }
1582        }
1583        return false;
1584    }
1585
1586    private boolean isSignalHeldAtStartOfSection(AllocationRequest ar) {
1587
1588        if (ar == null) {
1589            return false;
1590        }
1591
1592        Section sec = ar.getSection();
1593        ActiveTrain mActiveTrain = ar.getActiveTrain();
1594
1595        if (sec == null || mActiveTrain == null) {
1596            return false;
1597        }
1598
1599        Section lastSec = mActiveTrain.getLastAllocatedSection();
1600
1601        if (lastSec == null) {
1602            return false;
1603        }
1604
1605        if (!sec.equals(mActiveTrain.getNextSectionToAllocate())) {
1606            log.error("[{}]Allocation request section does not match active train next section to allocate",mActiveTrain.getActiveTrainName());
1607            log.error("[{}]Section requested {}",mActiveTrain.getActiveTrainName(), sec.getDisplayName(USERSYS));
1608            if (mActiveTrain.getNextSectionToAllocate() != null) {
1609                log.error("[{}]Section expected {}",
1610                        mActiveTrain.getActiveTrainName(), mActiveTrain.getNextSectionToAllocate().getDisplayName(USERSYS));
1611            }
1612            if (mActiveTrain.getLastAllocatedSection() != null) {
1613                log.error("[{}]Last Section Allocated {}",
1614                        mActiveTrain.getActiveTrainName(), mActiveTrain.getLastAllocatedSection().getDisplayName(USERSYS));
1615            }
1616            return false;
1617        }
1618
1619        Block facingBlock;
1620        Block protectingBlock;
1621        if (ar.getSectionDirection() == jmri.Section.FORWARD) {
1622            protectingBlock = sec.getBlockBySequenceNumber(0);
1623            facingBlock = lastSec.getBlockBySequenceNumber(lastSec.getNumBlocks() - 1);
1624        } else {
1625            // Reverse
1626            protectingBlock = sec.getBlockBySequenceNumber(sec.getNumBlocks() - 1);
1627            facingBlock = lastSec.getBlockBySequenceNumber(0);
1628        }
1629        if (protectingBlock == null || facingBlock == null) {
1630            return false;
1631        }
1632
1633        jmri.SignalMast sm = jmri.InstanceManager.getDefault(jmri.jmrit.display.layoutEditor.LayoutBlockManager.class)
1634                .getFacingSignalMast(facingBlock, protectingBlock);
1635        if (sm != null && sm.getHeld() && !_dispatcher.isMastHeldByDispatcher(sm, mActiveTrain)) {
1636            ar.setWaitingForSignalMast(sm);
1637            return true;
1638        }
1639        return false;
1640    }
1641
1642    private final static Logger log = LoggerFactory.getLogger(AutoAllocate.class);
1643
1644}