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