001package jmri.jmrit.operations.trains; 002 003import java.io.File; 004import java.text.SimpleDateFormat; 005 006import org.jdom2.*; 007import org.slf4j.Logger; 008import org.slf4j.LoggerFactory; 009 010import jmri.*; 011import jmri.jmrit.operations.OperationsManager; 012import jmri.jmrit.operations.OperationsXml; 013import jmri.jmrit.operations.automation.AutomationManager; 014import jmri.jmrit.operations.setup.Setup; 015import jmri.jmrit.operations.trains.schedules.TrainScheduleManager; 016import jmri.util.FileUtil; 017 018/** 019 * Loads and stores trains using xml files. Also stores various train parameters 020 * managed by the TrainManager. 021 * 022 * @author Daniel Boudreau Copyright (C) 2008, 2010, 2015 023 */ 024public class TrainManagerXml extends OperationsXml implements InstanceManagerAutoDefault, InstanceManagerAutoInitialize { 025 026 private boolean fileLoaded = false; 027 private String operationsFileName = "OperationsTrainRoster.xml";// NOI18N 028 029 private static final String BUILD_REPORT_FILE_NAME = Bundle.getMessage("train") + " ("; 030 private static final String MANIFEST_FILE_NAME = Bundle.getMessage("train") + " ("; 031 private static final String SWITCH_LIST_FILE_NAME = Bundle.getMessage("location") + " ("; 032 private static final String BACKUP_BUILD_REPORT_FILE_NAME = Bundle.getMessage("Report") + " " + Bundle.getMessage("train") + " ("; 033 private static final String FILE_TYPE_TXT = ").txt"; // NOI18N 034 private static final String FILE_TYPE_CSV = ").csv"; // NOI18N 035 036 // the directories under operations 037 static final String BUILD_STATUS = "buildstatus"; // NOI18N 038 static final String MANIFESTS = "manifests"; // NOI18N 039 static final String SWITCH_LISTS = "switchLists"; // NOI18N 040 public static final String CSV_MANIFESTS = "csvManifests"; // NOI18N 041 public static final String CSV_SWITCH_LISTS = "csvSwitchLists"; // NOI18N 042 static final String JSON_MANIFESTS = "jsonManifests"; // NOI18N 043 static final String MANIFESTS_BACKUPS = "manifestsBackups"; // NOI18N 044 static final String SWITCH_LISTS_BACKUPS = "switchListsBackups"; // NOI18N 045 static final String BUILD_STATUS_BACKUPS = "buildStatusBackups"; // NOI18N 046 047 public TrainManagerXml() { 048 } 049 050 @Override 051 public void writeFile(String name) throws java.io.FileNotFoundException, java.io.IOException { 052 log.debug("writeFile {}", name); 053 // This is taken in large part from "Java and XML" page 368 054 File file = findFile(name); 055 if (file == null) { 056 file = new File(name); 057 } 058 // create root element 059 Element root = new Element("operations-config"); // NOI18N 060 Document doc = newDocument(root, dtdLocation + "operations-trains.dtd"); // NOI18N 061 062 // add XSLT processing instruction 063 java.util.Map<String, String> m = new java.util.HashMap<>(); 064 m.put("type", "text/xsl"); // NOI18N 065 m.put("href", xsltLocation + "operations-trains.xsl"); // NOI18N 066 ProcessingInstruction p = new ProcessingInstruction("xml-stylesheet", m); // NOI18N 067 doc.addContent(0, p); 068 069 InstanceManager.getDefault(TrainManager.class).store(root); 070 InstanceManager.getDefault(TrainScheduleManager.class).store(root); 071 InstanceManager.getDefault(AutomationManager.class).store(root); 072 073 writeXML(file, doc); 074 075 // done - train file now stored, so can't be dirty 076 setDirty(false); 077 } 078 079 /** 080 * Read the contents of a roster XML file into this object. Note that this 081 * does not clear any existing entries. 082 */ 083 @Override 084 public void readFile(String name) throws org.jdom2.JDOMException, java.io.IOException { 085 086 // suppress rootFromName(name) warning message by checking to see if file exists 087 if (findFile(name) == null) { 088 log.debug("{} file could not be found", name); 089 fileLoaded = true; // set flag, could be the first time 090 return; 091 } 092 // find root 093 Element root = rootFromName(name); 094 if (root == null) { 095 log.debug("{} file could not be read", name); 096 return; 097 } 098 099 if (!root.getName().equals("operations-config")) { 100 log.warn("OperationsPro train file corrupted"); 101 return; 102 } 103 104 InstanceManager.getDefault(TrainManager.class).load(root); 105 InstanceManager.getDefault(TrainScheduleManager.class).load(root); 106 107 fileLoaded = true; // set flag trains are loaded 108 InstanceManager.getDefault(AutomationManager.class).load(root); 109 110 // now load train icons on panels 111 InstanceManager.getDefault(TrainManager.class).loadTrainIcons(); 112 113 log.debug("Trains have been loaded!"); 114 InstanceManager.getDefault(TrainLogger.class).enableTrainLogging(Setup.isTrainLoggerEnabled()); 115 116 for (Train train : InstanceManager.getDefault(TrainManager.class).getTrainsByIdList()) { 117 if (train.getStatusCode() == Train.CODE_BUILDING) { 118 log.warn("Reseting train ({}), was building when saved", train.getName()); 119 train.reset(); 120 } 121 } 122 123 setDirty(false); // clear dirty flag 124 125 // loading complete run startup scripts 126 InstanceManager.getDefault(TrainManager.class).runStartUpScripts(); 127 InstanceManager.getDefault(AutomationManager.class).runStartupAutomation(); 128 } 129 130 public boolean isTrainFileLoaded() { 131 return fileLoaded; 132 } 133 134 /** 135 * Store the train's build report 136 * 137 * @param name Full path name for train build report 138 * @return Build report File. 139 */ 140 public File createTrainBuildReportFile(String name) { 141 return createFile(defaultBuildReportFileName(name), false); // don't backup 142 } 143 144 public File getTrainBuildReportFile(String name) { 145 File file = new File(defaultBuildReportFileName(name)); 146 return file; 147 } 148 149 public String defaultBuildReportFileName(String name) { 150 return OperationsXml.getFileLocation() 151 + OperationsXml.getOperationsDirectoryName() 152 + File.separator 153 + BUILD_STATUS 154 + File.separator 155 + BUILD_REPORT_FILE_NAME 156 + name 157 + FILE_TYPE_TXT; // NOI18N 158 } 159 160 /** 161 * Creates the train's manifest file. 162 * 163 * @param name Full path name for manifest file. 164 * @return Manifest File. 165 */ 166 public File createTrainManifestFile(String name) { 167 savePreviousManifestFile(name); 168 return createFile(getDefaultManifestFileName(name), false); // don't backup 169 } 170 171 public File getTrainManifestFile(String name) { 172 File file = new File(getDefaultManifestFileName(name)); 173 return file; 174 } 175 176 public String getDefaultManifestFileName(String name) { 177 return OperationsXml.getFileLocation() 178 + OperationsXml.getOperationsDirectoryName() 179 + File.separator 180 + MANIFESTS 181 + File.separator 182 + MANIFEST_FILE_NAME 183 + name 184 + FILE_TYPE_TXT;// NOI18N 185 } 186 187 public String getBackupManifestFileName(String name, String lastModified) { 188 return getBackupManifestDirectoryName() 189 + name 190 + File.separator 191 + MANIFEST_FILE_NAME 192 + name 193 + ") " 194 + lastModified 195 + ".txt";// NOI18N 196 } 197 198 public String getBackupManifestDirectoryName() { 199 return OperationsXml.getFileLocation() 200 + OperationsXml.getOperationsDirectoryName() 201 + File.separator 202 + MANIFESTS_BACKUPS 203 + File.separator; 204 } 205 206 public String getBackupManifestDirectoryName(String name) { 207 return getBackupManifestDirectoryName() + File.separator + name + File.separator; 208 } 209 210 public String getBackupSwitchListFileName(String name, String lastModified) { 211 return getBackupSwitchListDirectoryName() 212 + name 213 + File.separator 214 + SWITCH_LIST_FILE_NAME 215 + name 216 + ") " 217 + lastModified 218 + ".txt";// NOI18N 219 } 220 221 public String getBackupSwitchListDirectoryName() { 222 return OperationsXml.getFileLocation() 223 + OperationsXml.getOperationsDirectoryName() 224 + File.separator 225 + SWITCH_LISTS_BACKUPS 226 + File.separator; 227 } 228 229 public String getBackupSwitchListDirectoryName(String name) { 230 return getBackupSwitchListDirectoryName() + File.separator + name + File.separator; 231 } 232 233 public String getBackupBuildStatusFileName(String name, String lastModified) { 234 return getBackupBuildStatusDirectoryName() 235 + name 236 + File.separator 237 + BACKUP_BUILD_REPORT_FILE_NAME 238 + name 239 + ") " 240 + lastModified 241 + ".txt";// NOI18N 242 } 243 244 public String getBackupBuildStatusDirectoryName() { 245 return OperationsXml.getFileLocation() 246 + OperationsXml.getOperationsDirectoryName() 247 + File.separator 248 + BUILD_STATUS_BACKUPS 249 + File.separator; 250 } 251 252 public String getBackupBuildStatusDirectoryName(String name) { 253 return getBackupBuildStatusDirectoryName() + File.separator + name + File.separator; 254 } 255 256 /** 257 * Store the CSV train manifest 258 * 259 * @param name Full path name to CSV train manifest file. 260 * @return Train CSV manifest File. 261 */ 262 public File createTrainCsvManifestFile(String name) { 263 return createFile(getDefaultCsvManifestFileName(name), false); // don't backup 264 } 265 266 public File getTrainCsvManifestFile(String name) { 267 File file = new File(getDefaultCsvManifestFileName(name)); 268 return file; 269 } 270 271 public String getDefaultCsvManifestFileName(String name) { 272 return getDefaultCsvManifestDirectory() + MANIFEST_FILE_NAME + name + FILE_TYPE_CSV; 273 } 274 275 private String getDefaultCsvManifestDirectory() { 276 return OperationsXml.getFileLocation() 277 + OperationsXml.getOperationsDirectoryName() 278 + File.separator 279 + CSV_MANIFESTS 280 + File.separator; 281 } 282 283 public void createDefaultCsvManifestDirectory() { 284 FileUtil.createDirectory(getDefaultCsvManifestDirectory()); 285 } 286 287 /** 288 * Store the Json manifest for a train 289 * 290 * @param name file name 291 * @param ext file extension to use 292 * @return Json manifest File 293 */ 294 public File createManifestFile(String name, String ext) { 295 return createFile(getDefaultManifestFileName(name, ext), false); // don't backup 296 } 297 298 public File getManifestFile(String name, String ext) { 299 return new File(getDefaultManifestFileName(name, ext)); 300 } 301 302 private String getDefaultManifestFileName(String name, String ext) { 303 return InstanceManager.getDefault(OperationsManager.class).getPath(JSON_MANIFESTS) + File.separator + "train-" + name + "." + ext; // NOI18N 304 } 305 306 /** 307 * Store the switch list for a location 308 * 309 * @param name The location's name, to become file name. 310 * @return Switch list File. 311 */ 312 public File createSwitchListFile(String name) { 313 savePreviousSwitchListFile(name); 314 return createFile(getDefaultSwitchListName(name), false); // don't backup 315 } 316 317 public File getSwitchListFile(String name) { 318 File file = new File(getDefaultSwitchListName(name)); 319 return file; 320 } 321 322 public String getDefaultSwitchListName(String name) { 323 return OperationsXml.getFileLocation() 324 + OperationsXml.getOperationsDirectoryName() 325 + File.separator 326 + SWITCH_LISTS 327 + File.separator 328 + SWITCH_LIST_FILE_NAME 329 + name 330 + FILE_TYPE_TXT; // NOI18N 331 } 332 333 /** 334 * Store the CSV switch list for a location 335 * 336 * @param name Location's name, to become file name. 337 * @return CSV switch list File. 338 */ 339 public File createCsvSwitchListFile(String name) { 340 return createFile(getDefaultCsvSwitchListFileName(name), true); // create backup 341 } 342 343 public File getCsvSwitchListFile(String name) { 344 File file = new File(getDefaultCsvSwitchListFileName(name)); 345 return file; 346 } 347 348 public String getDefaultCsvSwitchListFileName(String name) { 349 return getDefaultCsvSwitchListDirectoryName() + SWITCH_LIST_FILE_NAME + name + FILE_TYPE_CSV; 350 } 351 352 public String getDefaultCsvSwitchListDirectoryName() { 353 return OperationsXml.getFileLocation() 354 + OperationsXml.getOperationsDirectoryName() 355 + File.separator 356 + CSV_SWITCH_LISTS 357 + File.separator; 358 } 359 360 public void createDefaultCsvSwitchListDirectory() { 361 FileUtil.createDirectory(getDefaultCsvSwitchListDirectoryName()); 362 } 363 364 @Override 365 public void setOperationsFileName(String name) { 366 operationsFileName = name; 367 } 368 369 @Override 370 public String getOperationsFileName() { 371 return operationsFileName; 372 } 373 374 /** 375 * Save previous manifest file in a separate directory called 376 * manifestBackups. Each train manifest is saved in a unique directory using 377 * the train's name. 378 */ 379 private void savePreviousManifestFile(String name) { 380 if (Setup.isSaveTrainManifestsEnabled()) { 381 // create the manifest backup directory 382 createFile(getBackupManifestDirectoryName() + " ", false); // no backup 383 // now create unique backup directory for each train manifest 384 createFile(getBackupManifestDirectoryName(name) + " ", false); // no backup 385 // get old manifest file 386 File file = findFile(getDefaultManifestFileName(name)); 387 if (file == null) { 388 log.debug("No ({}) manifest file to backup", name); 389 } else if (file.canWrite()) { 390 String lastModified = new SimpleDateFormat("yyyyMMdd-HHmmssSSS").format(file.lastModified()); // NOI18N 391 String backupName = getBackupManifestFileName(name, lastModified); // NOI18N 392 if (file.renameTo(new File(backupName))) { 393 log.debug("created new manifest backup file {}", backupName); 394 } else { 395 log.error("could not create manifest backup file {}", backupName); 396 } 397 } 398 } 399 } 400 401 /** 402 * Save previous switch list file in a separate directory called 403 * switchListBackups. Each switch list is saved in a unique directory using 404 * the location's name. 405 */ 406 private void savePreviousSwitchListFile(String name) { 407 if (Setup.isSaveTrainManifestsEnabled()) { 408 // create the switch list backup directory 409 createFile(getBackupSwitchListDirectoryName() + " ", false); // no backup 410 // now create unique backup directory for location 411 createFile(getBackupSwitchListDirectoryName(name) + " ", false); // no backup 412 // get old switch list file 413 File file = findFile(getDefaultSwitchListName(name)); 414 if (file == null) { 415 log.debug("No ({}) switch list file to backup", name); 416 } else if (file.canRead()) { 417 String lastModified = new SimpleDateFormat("yyyyMMdd-HHmmssSSS").format(file.lastModified()); // NOI18N 418 String backupName = getBackupSwitchListFileName(name, lastModified); // NOI18N 419 File backupCopy = new File(backupName); 420 try { 421 FileUtil.copy(file, backupCopy); 422 log.debug("created new switch list backup file {}", backupName); 423 } catch (Exception e) { 424 log.error("could not create switch list backup file {}", backupName); 425 } 426 } 427 } 428 } 429 430 /** 431 * Save previous train build status file in a separate directory called 432 * BuildStatusBackups. Each build status is saved in a unique directory using 433 * the train's name. 434 * @param name train's name 435 */ 436 public void savePreviousBuildStatusFile(String name) { 437 if (Setup.isSaveTrainManifestsEnabled()) { 438 // create the build status backup directory 439 createFile(getBackupBuildStatusDirectoryName() + " ", false); // no backup 440 // now create unique backup directory for each train 441 createFile(getBackupBuildStatusDirectoryName(name) + " ", false); // no backup 442 // get old build status file for this train 443 File file = findFile(defaultBuildReportFileName(name)); 444 if (file == null) { 445 log.debug("No ({}) train build status file to backup", name); 446 } else if (file.canRead()) { 447 String lastModified = new SimpleDateFormat("yyyyMMdd-HHmmssSSS").format(file.lastModified()); // NOI18N 448 String backupName = getBackupBuildStatusFileName(name, lastModified); // NOI18N 449 File backupCopy = new File(backupName); 450 try { 451 FileUtil.copy(file, backupCopy); 452 log.debug("created new train build status backup file {}", backupName); 453 } catch (Exception e) { 454 log.error("could not create train build status backup file {}", backupName); 455 } 456 } 457 } 458 } 459 460 public void dispose() { 461 } 462 463 private final static Logger log = LoggerFactory.getLogger(TrainManagerXml.class); 464 465 @Override 466 public void initialize() { 467 load(); 468 } 469 470}