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