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}