001package jmri.jmrit.operations.trains; 002 003import org.slf4j.Logger; 004import org.slf4j.LoggerFactory; 005 006import jmri.jmrit.operations.locations.Track; 007import jmri.jmrit.operations.rollingstock.engines.Engine; 008import jmri.jmrit.operations.routes.Route; 009import jmri.jmrit.operations.routes.RouteLocation; 010import jmri.jmrit.operations.setup.Control; 011import jmri.jmrit.operations.setup.Setup; 012 013/** 014 * Contains methods for engines when building a train. 015 * 016 * @author Daniel Boudreau Copyright (C) 2022 017 */ 018public class TrainBuilderEngines extends TrainBuilderBase { 019 020 /** 021 * Builds a list of possible engines for this train. 022 */ 023 protected void getAndRemoveEnginesFromList() { 024 _engineList = engineManager.getAvailableTrainList(_train); 025 026 // remove any locos that the train can't use 027 for (int indexEng = 0; indexEng < _engineList.size(); indexEng++) { 028 Engine engine = _engineList.get(indexEng); 029 // remove engines types that train does not service 030 if (!_train.isTypeNameAccepted(engine.getTypeName())) { 031 addLine(_buildReport, SEVEN, Bundle.getMessage("buildExcludeEngineType", engine.toString(), 032 engine.getLocationName(), engine.getTrackName(), engine.getTypeName())); 033 _engineList.remove(indexEng--); 034 continue; 035 } 036 // remove engines with roads that train does not service 037 if (!_train.isLocoRoadNameAccepted(engine.getRoadName())) { 038 addLine(_buildReport, SEVEN, Bundle.getMessage("buildExcludeEngineRoad", engine.toString(), 039 engine.getLocationName(), engine.getTrackName(), engine.getRoadName())); 040 _engineList.remove(indexEng--); 041 continue; 042 } 043 // remove engines with owners that train does not service 044 if (!_train.isOwnerNameAccepted(engine.getOwnerName())) { 045 addLine(_buildReport, SEVEN, Bundle.getMessage("buildExcludeEngineOwner", engine.toString(), 046 engine.getLocationName(), engine.getTrackName(), engine.getOwnerName())); 047 _engineList.remove(indexEng--); 048 continue; 049 } 050 // remove engines with built dates that train does not service 051 if (!_train.isBuiltDateAccepted(engine.getBuilt())) { 052 addLine(_buildReport, SEVEN, Bundle.getMessage("buildExcludeEngineBuilt", engine.toString(), 053 engine.getLocationName(), engine.getTrackName(), engine.getBuilt())); 054 _engineList.remove(indexEng--); 055 continue; 056 } 057 // remove engines that are out of service 058 if (engine.isOutOfService()) { 059 addLine(_buildReport, SEVEN, Bundle.getMessage("buildExcludeEngineOutOfService", engine.toString(), 060 engine.getLocationName(), engine.getTrackName())); 061 _engineList.remove(indexEng--); 062 continue; 063 } 064 // remove engines that aren't on the train's route 065 if (_train.getRoute().getLastLocationByName(engine.getLocationName()) == null) { 066 log.debug("removing engine ({}) location ({}) not serviced by train", engine.toString(), 067 engine.getLocationName()); 068 _engineList.remove(indexEng--); 069 continue; 070 } 071 // is engine at interchange? 072 if (engine.getTrack().isInterchange()) { 073 // don't service a engine at interchange and has been dropped off 074 // by this train 075 if (engine.getTrack().getPickupOption().equals(Track.ANY) && 076 engine.getLastRouteId().equals(_train.getRoute().getId())) { 077 addLine(_buildReport, SEVEN, Bundle.getMessage("buildExcludeEngineDropByTrain", engine.toString(), 078 engine.getTypeName(), _train.getRoute().getName(), engine.getLocationName(), engine.getTrackName())); 079 _engineList.remove(indexEng--); 080 continue; 081 } 082 } 083 // is engine at interchange or spur and is this train allowed to pull? 084 if (engine.getTrack().isInterchange() || engine.getTrack().isSpur()) { 085 if (engine.getTrack().getPickupOption().equals(Track.TRAINS) || 086 engine.getTrack().getPickupOption().equals(Track.EXCLUDE_TRAINS)) { 087 if (engine.getTrack().isPickupTrainAccepted(_train)) { 088 log.debug("Engine ({}) can be picked up by this train", engine.toString()); 089 } else { 090 addLine(_buildReport, SEVEN, 091 Bundle.getMessage("buildExcludeEngineByTrain", engine.toString(), engine.getTypeName(), 092 engine.getTrack().getTrackTypeName(), engine.getLocationName(), engine.getTrackName())); 093 _engineList.remove(indexEng--); 094 continue; 095 } 096 } else if (engine.getTrack().getPickupOption().equals(Track.ROUTES) || 097 engine.getTrack().getPickupOption().equals(Track.EXCLUDE_ROUTES)) { 098 if (engine.getTrack().isPickupRouteAccepted(_train.getRoute())) { 099 log.debug("Engine ({}) can be picked up by this route", engine.toString()); 100 } else { 101 addLine(_buildReport, SEVEN, 102 Bundle.getMessage("buildExcludeEngineByRoute", engine.toString(), engine.getTypeName(), 103 engine.getTrack().getTrackTypeName(), engine.getLocationName(), engine.getTrackName())); 104 _engineList.remove(indexEng--); 105 continue; 106 } 107 } 108 } 109 } 110 } 111 112 /** 113 * Adds engines to the train starting at the first location in the train's 114 * route. Note that engines from staging are already part of the train. 115 * There can be up to two engine swaps in a train's route. 116 * 117 * @throws BuildFailedException if required engines can't be added to train. 118 */ 119 protected void addEnginesToTrain() throws BuildFailedException { 120 // allow up to two engine and caboose swaps in the train's route 121 RouteLocation engineTerminatesFirstLeg = _train.getTrainTerminatesRouteLocation(); 122 RouteLocation engineTerminatesSecondLeg = _train.getTrainTerminatesRouteLocation(); 123 124 // Adjust where the locos will terminate 125 if ((_train.getSecondLegOptions() & Train.CHANGE_ENGINES) == Train.CHANGE_ENGINES && 126 _train.getSecondLegStartRouteLocation() != null) { 127 engineTerminatesFirstLeg = _train.getSecondLegStartRouteLocation(); 128 } 129 if ((_train.getThirdLegOptions() & Train.CHANGE_ENGINES) == Train.CHANGE_ENGINES && 130 _train.getThirdLegStartRouteLocation() != null) { 131 engineTerminatesSecondLeg = _train.getThirdLegStartRouteLocation(); 132 // No engine or caboose change at first leg? 133 if ((_train.getSecondLegOptions() & Train.CHANGE_ENGINES) != Train.CHANGE_ENGINES) { 134 engineTerminatesFirstLeg = _train.getThirdLegStartRouteLocation(); 135 } 136 } 137 138 if (_train.getLeadEngine() == null) { 139 // option to remove locos from the train 140 if ((_train.getSecondLegOptions() & Train.REMOVE_ENGINES) == Train.REMOVE_ENGINES && 141 _train.getSecondLegStartRouteLocation() != null) { 142 addLine(_buildReport, THREE, BLANK_LINE); 143 addLine(_buildReport, THREE, 144 Bundle.getMessage("buildTrainRemoveEngines", _train.getSecondLegNumberEngines(), 145 _train.getSecondLegStartLocationName(), _train.getSecondLegEngineModel(), 146 _train.getSecondLegEngineRoad())); 147 if (getEngines(_train.getSecondLegNumberEngines(), _train.getSecondLegEngineModel(), 148 _train.getSecondLegEngineRoad(), _train.getTrainDepartsRouteLocation(), 149 _train.getSecondLegStartRouteLocation())) { 150 } else if (getConsist(_train.getSecondLegNumberEngines(), _train.getSecondLegEngineModel(), 151 _train.getSecondLegEngineRoad(), _train.getTrainDepartsRouteLocation(), 152 _train.getSecondLegStartRouteLocation())) { 153 } else { 154 throw new BuildFailedException(Bundle.getMessage("buildErrorEngines", 155 _train.getSecondLegNumberEngines(), _train.getTrainDepartsName(), 156 _train.getSecondLegStartRouteLocation().getLocation().getName())); 157 } 158 } 159 if ((_train.getThirdLegOptions() & Train.REMOVE_ENGINES) == Train.REMOVE_ENGINES && 160 _train.getThirdLegStartRouteLocation() != null) { 161 addLine(_buildReport, THREE, BLANK_LINE); 162 addLine(_buildReport, THREE, 163 Bundle.getMessage("buildTrainRemoveEngines", _train.getThirdLegNumberEngines(), 164 _train.getThirdLegStartLocationName(), _train.getThirdLegEngineModel(), 165 _train.getThirdLegEngineRoad())); 166 if (getEngines(_train.getThirdLegNumberEngines(), _train.getThirdLegEngineModel(), 167 _train.getThirdLegEngineRoad(), _train.getTrainDepartsRouteLocation(), 168 _train.getThirdLegStartRouteLocation())) { 169 } else if (getConsist(_train.getThirdLegNumberEngines(), _train.getThirdLegEngineModel(), 170 _train.getThirdLegEngineRoad(), _train.getTrainDepartsRouteLocation(), 171 _train.getThirdLegStartRouteLocation())) { 172 } else { 173 throw new BuildFailedException(Bundle.getMessage("buildErrorEngines", 174 _train.getThirdLegNumberEngines(), _train.getTrainDepartsName(), 175 _train.getThirdLegStartRouteLocation().getLocation().getName())); 176 } 177 } 178 // load engines at the start of the route for this train 179 addLine(_buildReport, THREE, BLANK_LINE); 180 if (getEngines(_train.getNumberEngines(), _train.getEngineModel(), _train.getEngineRoad(), 181 _train.getTrainDepartsRouteLocation(), engineTerminatesFirstLeg)) { 182 // when adding a caboose later in the route, no engine change 183 _secondLeadEngine = _lastEngine; 184 _thirdLeadEngine = _lastEngine; 185 } else if (getConsist(_train.getNumberEngines(), _train.getEngineModel(), _train.getEngineRoad(), 186 _train.getTrainDepartsRouteLocation(), engineTerminatesFirstLeg)) { 187 // when adding a caboose later in the route, no engine change 188 _secondLeadEngine = _lastEngine; 189 _thirdLeadEngine = _lastEngine; 190 } else { 191 throw new BuildFailedException(Bundle.getMessage("buildErrorEngines", _train.getNumberEngines(), 192 _train.getTrainDepartsName(), engineTerminatesFirstLeg.getName())); 193 } 194 } 195 196 // First engine change in route? 197 if ((_train.getSecondLegOptions() & Train.CHANGE_ENGINES) == Train.CHANGE_ENGINES || 198 (_train.getSecondLegOptions() & Train.ADD_ENGINES) == Train.ADD_ENGINES) { 199 addLine(_buildReport, THREE, BLANK_LINE); 200 if ((_train.getSecondLegOptions() & Train.CHANGE_ENGINES) == Train.CHANGE_ENGINES) { 201 addLine(_buildReport, THREE, 202 Bundle.getMessage("buildTrainEngineChange", _train.getSecondLegStartLocationName(), 203 _train.getSecondLegNumberEngines(), _train.getSecondLegEngineModel(), 204 _train.getSecondLegEngineRoad())); 205 } else { 206 addLine(_buildReport, THREE, 207 Bundle.getMessage("buildTrainAddEngines", _train.getSecondLegNumberEngines(), 208 _train.getSecondLegStartLocationName(), _train.getSecondLegEngineModel(), 209 _train.getSecondLegEngineRoad())); 210 } 211 if (getEngines(_train.getSecondLegNumberEngines(), _train.getSecondLegEngineModel(), 212 _train.getSecondLegEngineRoad(), _train.getSecondLegStartRouteLocation(), 213 engineTerminatesSecondLeg)) { 214 _secondLeadEngine = _lastEngine; 215 _thirdLeadEngine = _lastEngine; 216 } else if (getConsist(_train.getSecondLegNumberEngines(), _train.getSecondLegEngineModel(), 217 _train.getSecondLegEngineRoad(), _train.getSecondLegStartRouteLocation(), 218 engineTerminatesSecondLeg)) { 219 _secondLeadEngine = _lastEngine; 220 _thirdLeadEngine = _lastEngine; 221 } else { 222 throw new BuildFailedException( 223 Bundle.getMessage("buildErrorEngines", _train.getSecondLegNumberEngines(), 224 _train.getSecondLegStartRouteLocation(), engineTerminatesSecondLeg)); 225 } 226 } 227 // Second engine change in route? 228 if ((_train.getThirdLegOptions() & Train.CHANGE_ENGINES) == Train.CHANGE_ENGINES || 229 (_train.getThirdLegOptions() & Train.ADD_ENGINES) == Train.ADD_ENGINES) { 230 addLine(_buildReport, THREE, BLANK_LINE); 231 if ((_train.getThirdLegOptions() & Train.CHANGE_ENGINES) == Train.CHANGE_ENGINES) { 232 addLine(_buildReport, THREE, 233 Bundle.getMessage("buildTrainEngineChange", _train.getThirdLegStartLocationName(), 234 _train.getThirdLegNumberEngines(), _train.getThirdLegEngineModel(), 235 _train.getThirdLegEngineRoad())); 236 } else { 237 addLine(_buildReport, THREE, 238 Bundle.getMessage("buildTrainAddEngines", _train.getThirdLegNumberEngines(), 239 _train.getThirdLegStartLocationName(), _train.getThirdLegEngineModel(), 240 _train.getThirdLegEngineRoad())); 241 } 242 if (getEngines(_train.getThirdLegNumberEngines(), _train.getThirdLegEngineModel(), 243 _train.getThirdLegEngineRoad(), _train.getThirdLegStartRouteLocation(), 244 _train.getTrainTerminatesRouteLocation())) { 245 _thirdLeadEngine = _lastEngine; 246 } else if (getConsist(_train.getThirdLegNumberEngines(), _train.getThirdLegEngineModel(), 247 _train.getThirdLegEngineRoad(), _train.getThirdLegStartRouteLocation(), 248 _train.getTrainTerminatesRouteLocation())) { 249 _thirdLeadEngine = _lastEngine; 250 } else { 251 throw new BuildFailedException( 252 Bundle.getMessage("buildErrorEngines", Integer.parseInt(_train.getThirdLegNumberEngines()), 253 _train.getThirdLegStartRouteLocation(), _train.getTrainTerminatesRouteLocation())); 254 } 255 } 256 if (!_train.getNumberEngines().equals("0") && 257 (!_train.isBuildConsistEnabled() || Setup.getHorsePowerPerTon() == 0)) { 258 addLine(_buildReport, SEVEN, Bundle.getMessage("buildDoneAssingEnginesTrain", _train.getName())); 259 } 260 } 261 262 /** 263 * Checks to see if the engine or consist assigned to the train has the 264 * appropriate HP. If the train's HP requirements are significantly higher 265 * or lower than the engine that was assigned, the program will search for a 266 * more appropriate engine or consist, and assign that engine or consist to 267 * the train. The HP calculation is based on a minimum train speed of 36 268 * MPH. The formula HPT x 12 / % Grade = Speed, is used to determine the 269 * horsepower required. Speed is fixed at 36 MPH. For example a 1% grade 270 * requires a minimum of 3 HPT. Disabled for trains departing staging. 271 * 272 * @throws BuildFailedException if coding error. 273 */ 274 protected void checkEngineHP() throws BuildFailedException { 275 if (Setup.getHorsePowerPerTon() != 0) { 276 if (_train.getNumberEngines().equals(Train.AUTO_HPT)) { 277 checkEngineHP(_train.getLeadEngine(), _train.getEngineModel(), _train.getEngineRoad()); // 1st 278 // leg 279 } 280 if ((_train.getSecondLegOptions() & Train.CHANGE_ENGINES) == Train.CHANGE_ENGINES && 281 _train.getSecondLegNumberEngines().equals(Train.AUTO_HPT)) { 282 checkEngineHP(_secondLeadEngine, _train.getSecondLegEngineModel(), _train.getSecondLegEngineRoad()); 283 } 284 if ((_train.getThirdLegOptions() & Train.CHANGE_ENGINES) == Train.CHANGE_ENGINES && 285 _train.getThirdLegNumberEngines().equals(Train.AUTO_HPT)) { 286 checkEngineHP(_thirdLeadEngine, _train.getThirdLegEngineModel(), _train.getThirdLegEngineRoad()); 287 } 288 } 289 } 290 291 private void checkEngineHP(Engine leadEngine, String model, String road) throws BuildFailedException { 292 // code check 293 if (leadEngine == null) { 294 throw new BuildFailedException("ERROR coding issue, engine missing from checkEngineHP()"); 295 } 296 // departing staging? 297 if (leadEngine.getRouteLocation() == _train.getTrainDepartsRouteLocation() && _train.isDepartingStaging()) { 298 return; 299 } 300 addLine(_buildReport, ONE, BLANK_LINE); 301 addLine(_buildReport, ONE, 302 Bundle.getMessage("buildDetermineHpNeeded", leadEngine.toString(), leadEngine.getLocationName(), 303 leadEngine.getDestinationName(), _train.getTrainHorsePower(leadEngine.getRouteLocation()), 304 Setup.getHorsePowerPerTon())); 305 // now determine the HP needed for this train 306 double hpNeeded = 0; 307 int hpAvailable = 0; 308 Route route = _train.getRoute(); 309 if (route != null) { 310 boolean helper = false; 311 boolean foundStart = false; 312 for (RouteLocation rl : route.getLocationsBySequenceList()) { 313 if (!foundStart && rl != leadEngine.getRouteLocation()) { 314 continue; 315 } 316 foundStart = true; 317 if ((_train.getSecondLegOptions() == Train.HELPER_ENGINES && 318 rl == _train.getSecondLegStartRouteLocation()) || 319 (_train.getThirdLegOptions() == Train.HELPER_ENGINES && 320 rl == _train.getThirdLegStartRouteLocation())) { 321 addLine(_buildReport, FIVE, 322 Bundle.getMessage("AddHelpersAt", rl.getName())); 323 helper = true; 324 } 325 if ((_train.getSecondLegOptions() == Train.HELPER_ENGINES && 326 rl == _train.getSecondLegEndRouteLocation()) || 327 (_train.getThirdLegOptions() == Train.HELPER_ENGINES && 328 rl == _train.getThirdLegEndRouteLocation())) { 329 addLine(_buildReport, FIVE, 330 Bundle.getMessage("RemoveHelpersAt", rl.getName())); 331 helper = false; 332 } 333 if (helper) { 334 continue; // ignore HP needed when helpers are assigned to 335 // the train 336 } 337 // check for a change of engines in the train's route 338 if (rl == leadEngine.getRouteDestination()) { 339 log.debug("Remove loco ({}) at ({})", leadEngine.toString(), rl.getName()); 340 break; // done 341 } 342 if (_train.getTrainHorsePower(rl) > hpAvailable) 343 hpAvailable = _train.getTrainHorsePower(rl); 344 int weight = rl.getTrainWeight(); 345 double hpRequired = (Control.speedHpt * rl.getGrade() / 12) * weight; 346 if (hpRequired < Setup.getHorsePowerPerTon() * weight) 347 hpRequired = Setup.getHorsePowerPerTon() * weight; // min HPT 348 if (hpRequired > hpNeeded) { 349 addLine(_buildReport, SEVEN, 350 Bundle.getMessage("buildReportTrainHpNeeds", weight, _train.getNumberCarsInTrain(rl), 351 rl.getGrade(), rl.getName(), rl.getId(), hpRequired)); 352 hpNeeded = hpRequired; 353 } 354 } 355 } 356 if (hpNeeded > hpAvailable) { 357 addLine(_buildReport, ONE, 358 Bundle.getMessage("buildAssignedHpNotEnough", leadEngine.toString(), hpAvailable, hpNeeded)); 359 getNewEngine((int) hpNeeded, leadEngine, model, road); 360 } else if (hpAvailable > 2 * hpNeeded) { 361 addLine(_buildReport, ONE, 362 Bundle.getMessage("buildAssignedHpTooMuch", leadEngine.toString(), hpAvailable, hpNeeded)); 363 getNewEngine((int) hpNeeded, leadEngine, model, road); 364 } else { 365 log.debug("Keeping engine ({}) it meets the train's HP requirement", leadEngine.toString()); 366 } 367 } 368 369 /** 370 * Checks to see if additional engines are needed for the train based on the 371 * train's calculated tonnage. Minimum speed for the train is fixed at 36 372 * MPH. The formula HPT x 12 / % Grade = Speed, is used to determine the 373 * horsepower needed. For example a 1% grade requires a minimum of 3 HPT. 374 * 375 * Ignored when departing staging 376 * 377 * @throws BuildFailedException if build failure 378 */ 379 protected void checkNumnberOfEnginesNeededHPT() throws BuildFailedException { 380 if (!_train.isBuildConsistEnabled() || 381 Setup.getHorsePowerPerTon() == 0) { 382 return; 383 } 384 addLine(_buildReport, ONE, BLANK_LINE); 385 addLine(_buildReport, ONE, Bundle.getMessage("buildDetermineNeeds", Setup.getHorsePowerPerTon())); 386 Route route = _train.getRoute(); 387 int hpAvailable = 0; 388 int extraHpNeeded = 0; 389 RouteLocation rlNeedHp = null; 390 RouteLocation rlStart = _train.getTrainDepartsRouteLocation(); 391 RouteLocation rlEnd = _train.getTrainTerminatesRouteLocation(); 392 boolean departingStaging = _train.isDepartingStaging(); 393 if (route != null) { 394 boolean helper = false; 395 for (RouteLocation rl : route.getLocationsBySequenceList()) { 396 if ((_train.getSecondLegOptions() == Train.HELPER_ENGINES && 397 rl == _train.getSecondLegStartRouteLocation()) || 398 (_train.getThirdLegOptions() == Train.HELPER_ENGINES && 399 rl == _train.getThirdLegStartRouteLocation())) { 400 addLine(_buildReport, FIVE, Bundle.getMessage("AddHelpersAt", rl.getName())); 401 helper = true; 402 } 403 if ((_train.getSecondLegOptions() == Train.HELPER_ENGINES && 404 rl == _train.getSecondLegEndRouteLocation()) || 405 (_train.getThirdLegOptions() == Train.HELPER_ENGINES && 406 rl == _train.getThirdLegEndRouteLocation())) { 407 addLine(_buildReport, FIVE, 408 Bundle.getMessage("RemoveHelpersAt", rl.getName())); 409 helper = false; 410 } 411 if (helper) { 412 continue; 413 } 414 // check for a change of engines in the train's route 415 if (((_train.getSecondLegOptions() & Train.CHANGE_ENGINES) == Train.CHANGE_ENGINES && 416 rl == _train.getSecondLegStartRouteLocation()) || 417 ((_train.getThirdLegOptions() & Train.CHANGE_ENGINES) == Train.CHANGE_ENGINES && 418 rl == _train.getThirdLegStartRouteLocation())) { 419 log.debug("Loco change at ({})", rl.getName()); 420 addEnginesBasedHPT(hpAvailable, extraHpNeeded, rlNeedHp, rlStart, rl); 421 addLine(_buildReport, THREE, BLANK_LINE); 422 // reset for next leg of train's route 423 rlStart = rl; 424 rlNeedHp = null; 425 extraHpNeeded = 0; 426 departingStaging = false; 427 } 428 if (departingStaging) { 429 continue; 430 } 431 double weight = rl.getTrainWeight(); 432 if (weight > 0) { 433 double hptMinimum = Setup.getHorsePowerPerTon(); 434 double hptGrade = (Control.speedHpt * rl.getGrade() / 12); 435 double hp = _train.getTrainHorsePower(rl); 436 double hpt = hp / weight; 437 if (hptGrade > hptMinimum) { 438 hptMinimum = hptGrade; 439 } 440 if (hptMinimum > hpt) { 441 int addHp = (int) (hptMinimum * weight - hp); 442 if (addHp > extraHpNeeded) { 443 hpAvailable = (int) hp; 444 extraHpNeeded = addHp; 445 rlNeedHp = rl; 446 } 447 addLine(_buildReport, SEVEN, 448 Bundle.getMessage("buildAddLocosStatus", weight, hp, Control.speedHpt, rl.getGrade(), 449 hpt, hptMinimum, rl.getName(), rl.getId())); 450 addLine(_buildReport, FIVE, 451 Bundle.getMessage("buildTrainRequiresAddHp", addHp, rl.getName(), hptMinimum)); 452 } 453 } 454 } 455 } 456 addEnginesBasedHPT(hpAvailable, extraHpNeeded, rlNeedHp, rlStart, rlEnd); 457 addLine(_buildReport, SEVEN, Bundle.getMessage("buildDoneAssingEnginesTrain", _train.getName())); 458 addLine(_buildReport, THREE, BLANK_LINE); 459 } 460 461 private final static Logger log = LoggerFactory.getLogger(TrainBuilderEngines.class); 462}