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}