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