001package jmri.jmrit.operations.automation; 002 003import java.beans.PropertyChangeListener; 004import java.util.*; 005 006import javax.swing.JComboBox; 007 008import org.jdom2.Attribute; 009import org.jdom2.Element; 010import org.slf4j.Logger; 011import org.slf4j.LoggerFactory; 012 013import jmri.*; 014import jmri.beans.PropertyChangeSupport; 015import jmri.jmrit.operations.rollingstock.cars.CarManagerXml; 016import jmri.jmrit.operations.rollingstock.engines.EngineManagerXml; 017import jmri.jmrit.operations.setup.Control; 018import jmri.jmrit.operations.setup.OperationsSetupXml; 019import jmri.jmrit.operations.trains.TrainManagerXml; 020 021/** 022 * Manages automations. 023 * 024 * @author Bob Jacobsen Copyright (C) 2003 025 * @author Daniel Boudreau Copyright (C) 2016 026 */ 027public class AutomationManager extends PropertyChangeSupport implements InstanceManagerAutoDefault, InstanceManagerAutoInitialize, PropertyChangeListener { 028 029 public static final String LISTLENGTH_CHANGED_PROPERTY = "automationListLength"; // NOI18N 030 private int _id = 0; // retain highest automation Id seen to ensure no Id 031 // collisions 032 033 public AutomationManager() { 034 } 035 036 // stores known Automation instances by id 037 protected Hashtable<String, Automation> _automationHashTable = new Hashtable<>(); 038 039 protected Automation _startupAutomation; 040 041 /** 042 * @return Number of automations 043 */ 044 public int getSize() { 045 return _automationHashTable.size(); 046 } 047 048 /** 049 * @param name The string name of the automation to be returned. 050 * @return requested Automation object or null if none exists 051 */ 052 public Automation getAutomationByName(String name) { 053 Automation automation; 054 Enumeration<Automation> en = _automationHashTable.elements(); 055 while (en.hasMoreElements()) { 056 automation = en.nextElement(); 057 if (automation.getName().equals(name)) { 058 return automation; 059 } 060 } 061 return null; 062 } 063 064 public Automation getAutomationById(String id) { 065 return _automationHashTable.get(id); 066 } 067 068 /** 069 * Finds an existing automation or creates a new automation if needed 070 * requires automation's name creates a unique id for this automation 071 * 072 * @param name The string name of the automation. 073 * @return new automation or existing automation 074 */ 075 public Automation newAutomation(String name) { 076 Automation automation = getAutomationByName(name); 077 if (automation == null) { 078 _id++; 079 automation = new Automation(Integer.toString(_id), name); 080 int oldSize = _automationHashTable.size(); 081 _automationHashTable.put(automation.getId(), automation); 082 setDirtyAndFirePropertyChange(LISTLENGTH_CHANGED_PROPERTY, oldSize, _automationHashTable.size()); 083 } 084 return automation; 085 } 086 087 /** 088 * Remember a NamedBean Object created outside the manager. 089 * 090 * @param automation The automation that is being registered. 091 */ 092 public void register(Automation automation) { 093 int oldSize = _automationHashTable.size(); 094 _automationHashTable.put(automation.getId(), automation); 095 // find last id created 096 int id = Integer.parseInt(automation.getId()); 097 if (id > _id) { 098 _id = id; 099 } 100 setDirtyAndFirePropertyChange(LISTLENGTH_CHANGED_PROPERTY, oldSize, _automationHashTable.size()); 101 } 102 103 /** 104 * Forget a NamedBean Object created outside the manager. 105 * 106 * @param automation The automation to be deleted. 107 */ 108 public void deregister(Automation automation) { 109 if (automation == null) { 110 return; 111 } 112 automation.dispose(); 113 int oldSize = _automationHashTable.size(); 114 _automationHashTable.remove(automation.getId()); 115 setDirtyAndFirePropertyChange(LISTLENGTH_CHANGED_PROPERTY, oldSize, _automationHashTable.size()); 116 } 117 118 /** 119 * Sort by automation name 120 * 121 * @return list of automations ordered by name 122 */ 123 public List<Automation> getAutomationsByNameList() { 124 List<Automation> sortList = getList(); 125 // now re-sort 126 List<Automation> out = new ArrayList<>(); 127 for (Automation automation : sortList) { 128 for (int j = 0; j < out.size(); j++) { 129 if (automation.getName().compareToIgnoreCase(out.get(j).getName()) < 0) { 130 out.add(j, automation); 131 break; 132 } 133 } 134 if (!out.contains(automation)) { 135 out.add(automation); 136 } 137 } 138 return out; 139 140 } 141 142 /** 143 * Sort by automation id number 144 * 145 * @return list of automations ordered by id number 146 */ 147 public List<Automation> getAutomationsByIdList() { 148 List<Automation> sortList = getList(); 149 // now re-sort 150 List<Automation> out = new ArrayList<>(); 151 for (Automation automation : sortList) { 152 for (int j = 0; j < out.size(); j++) { 153 try { 154 if (Integer.parseInt(automation.getId()) < Integer.parseInt(out.get(j).getId())) { 155 out.add(j, automation); 156 break; 157 } 158 } catch (NumberFormatException e) { 159 log.debug("list id number isn't a number"); 160 } 161 } 162 if (!out.contains(automation)) { 163 out.add(automation); 164 } 165 } 166 return out; 167 } 168 169 private List<Automation> getList() { 170 List<Automation> out = new ArrayList<>(); 171 Enumeration<Automation> en = _automationHashTable.elements(); 172 while (en.hasMoreElements()) { 173 out.add(en.nextElement()); 174 } 175 return out; 176 } 177 178 /** 179 * Gets a JComboBox loaded with automations. 180 * 181 * @return JComboBox with a list of automations. 182 */ 183 public JComboBox<Automation> getComboBox() { 184 JComboBox<Automation> box = new JComboBox<>(); 185 updateComboBox(box); 186 return box; 187 } 188 189 /** 190 * Update a JComboBox with the latest automations. 191 * 192 * @param box the JComboBox needing an update. 193 */ 194 public void updateComboBox(JComboBox<Automation> box) { 195 box.removeAllItems(); 196 box.addItem(null); 197 for (Automation automation : getAutomationsByNameList()) { 198 box.addItem(automation); 199 } 200 } 201 202 /** 203 * Restarts all automations that were running when the operations program 204 * was last saved. 205 */ 206 public void resumeAutomations() { 207 for (Automation automation : getAutomationsByNameList()) { 208 if (!automation.isActionRunning() && !automation.isReadyToRun()) { 209 automation.resume(); 210 } 211 } 212 } 213 214 /** 215 * Makes a new copy of automation 216 * 217 * @param automation the automation to copy 218 * @param newName name for the copy of automation 219 * @return new copy of automation 220 */ 221 public Automation copyAutomation(Automation automation, String newName) { 222 Automation newAutomation = newAutomation(newName); 223 newAutomation.copyAutomation(automation); 224 return newAutomation; 225 } 226 227 public Automation getStartupAutomation() { 228 return _startupAutomation; 229 } 230 231 protected String getStartupAutomationId() { 232 String id = ""; 233 if (getStartupAutomation() != null) { 234 id = getStartupAutomation().getId(); 235 } 236 return id; 237 } 238 239 public void setStartupAutomation(Automation automation) { 240 Automation old = _startupAutomation; 241 _startupAutomation = automation; 242 setDirtyAndFirePropertyChange("automationStartupIdChanged", old, automation); 243 } 244 245 public void runStartupAutomation() { 246 Automation startup = getStartupAutomation(); 247 if (startup != null) { 248 log.debug("Run automation: {}", startup.getName()); 249 startup.run(); 250 } 251 } 252 253 public void dispose() { 254 _automationHashTable.clear(); 255 _id = 0; 256 } 257 258 /** 259 * Construct this Entry from XML. This member has to remain synchronized 260 * with the detailed DTD in operations-trains.dtd 261 * 262 * @param root Consist XML element 263 */ 264 public void load(Element root) { 265 if (root.getChild(Xml.AUTOMATIONS) != null) { 266 List<Element> eAutomations = root.getChild(Xml.AUTOMATIONS).getChildren(Xml.AUTOMATION); 267 log.debug("readFile sees {} automations", eAutomations.size()); 268 for (Element eAutomation : eAutomations) { 269 register(new Automation(eAutomation)); 270 } 271 } 272 // get startup automation after all of the automations have been loaded 273 Element e = root.getChild(Xml.AUTOMATION_OPTIONS); 274 Attribute a; 275 if (e != null) { 276 if ((a = e.getAttribute(Xml.AUTOMATION_STARTUP_ID)) != null) { 277 _startupAutomation = getAutomationById(a.getValue()); 278 } 279 } 280 } 281 282 /** 283 * Create an XML element to represent this Entry. This member has to remain 284 * synchronized with the detailed DTD in operations-trains.dtd. 285 * 286 * @param root Contents in a JDOM Element 287 */ 288 public void store(Element root) { 289 Element e = new Element(Xml.AUTOMATION_OPTIONS); 290 e.setAttribute(Xml.AUTOMATION_STARTUP_ID, getStartupAutomationId()); 291 root.addContent(e); 292 Element values; 293 root.addContent(values = new Element(Xml.AUTOMATIONS)); 294 // add entries 295 for (Automation automation : getAutomationsByNameList()) { 296 values.addContent(automation.store()); 297 } 298 } 299 300 @Override 301 public void propertyChange(java.beans.PropertyChangeEvent e) { 302 if (Control.SHOW_PROPERTY) { 303 log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(), e 304 .getNewValue()); 305 } 306 } 307 308 protected void setDirtyAndFirePropertyChange(String p, Object old, Object n) { 309 // set dirty 310 InstanceManager.getDefault(TrainManagerXml.class).setDirty(true); 311 firePropertyChange(p, old, n); 312 } 313 314 @Override 315 public void initialize() { 316 InstanceManager.getDefault(OperationsSetupXml.class); // load setup 317 InstanceManager.getDefault(CarManagerXml.class); // load cars 318 InstanceManager.getDefault(EngineManagerXml.class); // load engines 319 InstanceManager.getDefault(TrainManagerXml.class); // load trains 320 } 321 322 private static final Logger log = LoggerFactory.getLogger(AutomationManager.class); 323 324}