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