001package jmri.jmrit.operations.rollingstock.engines; 002 003import java.beans.PropertyChangeEvent; 004import java.util.*; 005 006import javax.swing.JComboBox; 007 008import org.jdom2.Element; 009import org.slf4j.Logger; 010import org.slf4j.LoggerFactory; 011 012import jmri.*; 013import jmri.jmrit.operations.OperationsPanel; 014import jmri.jmrit.operations.locations.Track; 015import jmri.jmrit.operations.rollingstock.RollingStock; 016import jmri.jmrit.operations.rollingstock.RollingStockManager; 017import jmri.jmrit.operations.setup.OperationsSetupXml; 018import jmri.jmrit.operations.trains.Train; 019import jmri.jmrit.operations.trains.TrainManifestHeaderText; 020 021/** 022 * Manages the engines. 023 * 024 * @author Daniel Boudreau Copyright (C) 2008 025 */ 026public class EngineManager extends RollingStockManager<Engine> 027 implements InstanceManagerAutoDefault, InstanceManagerAutoInitialize { 028 029 public EngineManager() { 030 } 031 032 /** 033 * Finds an existing engine or creates a new engine if needed requires engine's 034 * road and number 035 * 036 * @param engineRoad The engine's road initials 037 * @param engineNumber The engine's road number 038 * 039 * @return new engine or existing engine 040 */ 041 @Override 042 public Engine newRS(String engineRoad, String engineNumber) { 043 Engine engine = getByRoadAndNumber(engineRoad, engineNumber); 044 if (engine == null) { 045 engine = new Engine(engineRoad, engineNumber); 046 register(engine); 047 } 048 return engine; 049 } 050 051 @Override 052 public void deregister(Engine engine) { 053 super.deregister(engine); 054 InstanceManager.getDefault(EngineManagerXml.class).setDirty(true); 055 } 056 057 /** 058 * Sort by engine model 059 * 060 * @return list of engines ordered by engine model 061 */ 062 public List<Engine> getByModelList() { 063 return getByList(getByRoadNameList(), BY_MODEL); 064 } 065 066 /** 067 * Sort by engine consist 068 * 069 * @return list of engines ordered by engine consist 070 */ 071 public List<Engine> getByConsistList() { 072 return getByList(getByRoadNameList(), BY_CONSIST); 073 } 074 075 public List<Engine> getByHpList() { 076 return getByList(getByModelList(), BY_HP); 077 } 078 079 // The special sort options for engines 080 private static final int BY_MODEL = 30; 081 private static final int BY_CONSIST = 31; 082 private static final int BY_HP = 32; 083 084 // add engine options to sort comparator 085 @Override 086 protected java.util.Comparator<Engine> getComparator(int attribute) { 087 switch (attribute) { 088 case BY_MODEL: 089 return (e1, e2) -> (e1.getModel().compareToIgnoreCase(e2.getModel())); 090 case BY_CONSIST: 091 return (e1, e2) -> (e1.getConsistName().compareToIgnoreCase(e2.getConsistName())); 092 case BY_HP: 093 return (e1, e2) -> (e1.getHpInteger() - e2.getHpInteger()); 094 default: 095 return super.getComparator(attribute); 096 } 097 } 098 099 /** 100 * Returns a list of available engines (no assigned train). Engines are 101 * ordered by track priority and least recently moved to most recently 102 * moved. 103 * 104 * @param train The Train requesting this list. 105 * @return Ordered list of engines not assigned to a train 106 */ 107 public List<Engine> getAvailableTrainList(Train train) { 108 // now build list of available engines for this route 109 List<Engine> out = new ArrayList<>(); 110 // get engines by track priority and moves 111 List<Engine> sortByPriority = sortByTrackPriority(getByMovesList()); 112 for (Engine engine : sortByPriority) { 113 if (engine.getTrack() != null && (engine.getTrain() == null || engine.getTrain() == train)) { 114 out.add(engine); 115 } 116 } 117 return out; 118 } 119 120 /** 121 * Returns a list of locos sorted by blocking number for a train. This returns a 122 * list of consisted locos in the order that they were entered in. 123 * 124 * @param train The Train requesting this list. 125 * @return A list of sorted locos. 126 */ 127 public List<Engine> getByTrainBlockingList(Train train) { 128 return getByList(super.getByTrainList(train), BY_BLOCKING); 129 } 130 131 /** 132 * Get a list of engine road names. 133 * 134 * @param model The string model name, can be NONE. 135 * 136 * @return List of engine road names. 137 */ 138 public List<String> getEngineRoadNames(String model) { 139 List<String> names = new ArrayList<>(); 140 Enumeration<String> en = _hashTable.keys(); 141 while (en.hasMoreElements()) { 142 Engine engine = getById(en.nextElement()); 143 if ((engine.getModel().equals(model) || model.equals(NONE)) && !names.contains(engine.getRoadName())) { 144 names.add(engine.getRoadName()); 145 } 146 } 147 java.util.Collections.sort(names); 148 return names; 149 } 150 151 public void updateEngineRoadComboBox(String engineModel, JComboBox<String> roadEngineBox) { 152 roadEngineBox.removeAllItems(); 153 roadEngineBox.addItem(NONE); 154 List<String> roads = getEngineRoadNames(engineModel); 155 for (String roadName : roads) { 156 roadEngineBox.addItem(roadName); 157 } 158 OperationsPanel.padComboBox(roadEngineBox); 159 } 160 161 int _commentLength = 0; 162 163 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( value="SLF4J_FORMAT_SHOULD_BE_CONST", 164 justification="I18N of Info Message") 165 public int getMaxCommentLength() { 166 if (_commentLength == 0) { 167 _commentLength = TrainManifestHeaderText.getStringHeader_Comment().length(); 168 String comment = ""; 169 Engine engineMax = null; 170 for (Engine engine : getList()) { 171 if (engine.getComment().length() > _commentLength) { 172 _commentLength = engine.getComment().length(); 173 comment = engine.getComment(); 174 engineMax = engine; 175 } 176 } 177 if (engineMax != null) { 178 log.info(Bundle.getMessage("InfoMaxComment", engineMax.toString(), comment, _commentLength)); 179 } 180 } 181 return _commentLength; 182 } 183 184 /** 185 * Creates a clone for the engine, and clones if the engine is part of a consist. 186 * Note that a engine have have multiple clones. 187 * 188 * @param engine The engine to clone 189 * @param track The destination track for the clones 190 * @param train The train transporting the clones 191 * @param startTime The date and time the clones were moved 192 * @return clone for this engine 193 */ 194 public Engine createClone(Engine engine, Track track, Train train, Date startTime) { 195 int cloneCreationOrder = getCloneCreationOrder(); 196 Engine cloneEng = engine.copy(); 197 cloneEng.setNumber(engine.getNumber() + Engine.CLONE + cloneCreationOrder); 198 cloneEng.setClone(true); 199 // register engine before setting location so the engine gets logged 200 register(cloneEng); 201 cloneEng.setLocation(engine.getLocation(), engine.getTrack(), RollingStock.FORCE); 202 // for reset 203 cloneEng.setLastRouteId(engine.getLastRouteId()); 204 cloneEng.setMoves(engine.getMoves()); 205 if (engine.getConsist() != null) { 206 String consistName = engine.getConsistName() + Engine.CLONE + cloneCreationOrder; 207 Consist consist = InstanceManager.getDefault(ConsistManager.class).newConsist(consistName); 208 cloneEng.setConsist(consist); 209 for (Engine e : engine.getConsist().getEngines()) { 210 if (e != engine) { 211 Engine nEng = e.copy(); 212 nEng.setNumber(e.getNumber() + Engine.CLONE + cloneCreationOrder); 213 nEng.setClone(true); 214 nEng.setConsist(consist); 215 nEng.setMoves(e.getMoves()); 216 register(nEng); 217 nEng.setLocation(engine.getLocation(), engine.getTrack(), RollingStock.FORCE); 218 // for reset 219 // move engine to new location for later pick up 220 e.setLocation(track.getLocation(), track, RollingStock.FORCE); 221 e.setLastTrain(train); 222 e.setLastLocationId(engine.getLocationId()); 223 e.setLastTrackId(engine.getTrackId()); 224 e.setLastDate(startTime); 225 e.setMoves(e.getMoves() + 1); // bump count 226 e.setCloneOrder(cloneCreationOrder); // for reset 227 } 228 } 229 } 230 // move engine to new location for later pick up 231 engine.setLocation(track.getLocation(), track, RollingStock.FORCE); 232 engine.setLastTrain(train); 233 engine.setLastLocationId(cloneEng.getLocationId()); 234 engine.setLastTrackId(cloneEng.getTrackId()); 235 engine.setLastRouteId(train.getRoute().getId()); 236 // this engine was moved during the build process 237 engine.setLastDate(startTime); 238 engine.setMoves(engine.getMoves() + 1); // bump count 239 engine.setCloneOrder(cloneCreationOrder); // for reset 240 engine.setDestination(null, null); 241 return cloneEng; 242 } 243 244 public void load(Element root) { 245 if (root.getChild(Xml.ENGINES) != null) { 246 List<Element> engines = root.getChild(Xml.ENGINES).getChildren(Xml.ENGINE); 247 log.debug("readFile sees {} engines", engines.size()); 248 for (Element e : engines) { 249 register(new Engine(e)); 250 } 251 } 252 } 253 254 /** 255 * Create an XML element to represent this Entry. This member has to remain 256 * synchronized with the detailed DTD in operations-engines.dtd. 257 * 258 * @param root The common Element for operations-engines.dtd. 259 * 260 */ 261 public void store(Element root) { 262 Element values; 263 root.addContent(values = new Element(Xml.ENGINES)); 264 // add entries 265 for (RollingStock rs : getByRoadNameList()) { 266 Engine eng = (Engine) rs; 267 values.addContent(eng.store()); 268 } 269 } 270 271 protected void setDirtyAndFirePropertyChange(String p, Object old, Object n) { 272 // Set dirty 273 InstanceManager.getDefault(EngineManagerXml.class).setDirty(true); 274 super.firePropertyChange(p, old, n); 275 } 276 277 @Override 278 public void propertyChange(PropertyChangeEvent evt) { 279 if (evt.getPropertyName().equals(Engine.COMMENT_CHANGED_PROPERTY)) { 280 _commentLength = 0; 281 } 282 super.propertyChange(evt); 283 } 284 285 private final static Logger log = LoggerFactory.getLogger(EngineManager.class); 286 287 @Override 288 public void initialize() { 289 InstanceManager.getDefault(OperationsSetupXml.class); // load setup 290 // create manager to load engines and their attributes 291 InstanceManager.getDefault(EngineManagerXml.class); 292 } 293}