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