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