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 log.debug("Trains have been loaded!"); 111 112 for (Train train : InstanceManager.getDefault(TrainManager.class).getTrainsByIdList()) { 113 if (train.getStatusCode() == Train.CODE_BUILDING) { 114 log.warn("Reseting train ({}), was building when saved", train.getName()); 115 train.reset(); 116 } 117 } 118 119 setDirty(false); // clear dirty flag 120 121 // loading complete run startup scripts 122 InstanceManager.getDefault(TrainManager.class).runStartUpScripts(); 123 InstanceManager.getDefault(AutomationManager.class).runStartupAutomation(); 124 } 125 126 public boolean isTrainFileLoaded() { 127 return fileLoaded; 128 } 129 130 /** 131 * Store the train's build report 132 * 133 * @param name Full path name for train build report 134 * @return Build report File. 135 */ 136 public File createTrainBuildReportFile(String name) { 137 return createFile(defaultBuildReportFileName(name)); // don't backup 138 } 139 140 public File getTrainBuildReportFile(String name) { 141 File file = new File(defaultBuildReportFileName(name)); 142 return file; 143 } 144 145 public String defaultBuildReportFileName(String name) { 146 return OperationsXml.getFileLocation() 147 + OperationsXml.getOperationsDirectoryName() 148 + File.separator 149 + BUILD_STATUS 150 + File.separator 151 + BUILD_REPORT_FILE_NAME 152 + name 153 + FILE_TYPE_TXT; // NOI18N 154 } 155 156 /** 157 * Creates the train's manifest file. 158 * 159 * @param name Full path name for manifest file. 160 * @return Manifest File. 161 */ 162 public File createTrainManifestFile(String name) { 163 savePreviousManifestFile(name); 164 return createFile(getDefaultManifestFileName(name)); // don't backup 165 } 166 167 public File getTrainManifestFile(String name) { 168 File file = new File(getDefaultManifestFileName(name)); 169 return file; 170 } 171 172 public String getDefaultManifestFileName(String name) { 173 return OperationsXml.getFileLocation() 174 + OperationsXml.getOperationsDirectoryName() 175 + File.separator 176 + MANIFESTS 177 + File.separator 178 + MANIFEST_FILE_NAME 179 + name 180 + FILE_TYPE_TXT;// NOI18N 181 } 182 183 public String getBackupManifestFileName(String name, String lastModified) { 184 return getBackupManifestDirectoryName() 185 + name 186 + File.separator 187 + MANIFEST_FILE_NAME 188 + name 189 + ") " 190 + lastModified 191 + ".txt";// NOI18N 192 } 193 194 public String getBackupManifestDirectoryName() { 195 return OperationsXml.getFileLocation() 196 + OperationsXml.getOperationsDirectoryName() 197 + File.separator 198 + MANIFESTS_BACKUPS 199 + File.separator; 200 } 201 202 public String getBackupManifestDirectoryName(String name) { 203 return getBackupManifestDirectoryName() + name + File.separator; 204 } 205 206 public String getBackupSwitchListFileName(String name, String lastModified) { 207 return getBackupSwitchListDirectoryName() 208 + name 209 + File.separator 210 + SWITCH_LIST_FILE_NAME 211 + name 212 + ") " 213 + lastModified 214 + ".txt";// NOI18N 215 } 216 217 public String getBackupSwitchListDirectoryName() { 218 return OperationsXml.getFileLocation() 219 + OperationsXml.getOperationsDirectoryName() 220 + File.separator 221 + SWITCH_LISTS_BACKUPS 222 + File.separator; 223 } 224 225 public String getBackupSwitchListDirectoryName(String name) { 226 return getBackupSwitchListDirectoryName() + name + File.separator; 227 } 228 229 public String getBackupBuildStatusFileName(String name, String lastModified) { 230 return getBackupBuildStatusDirectoryName() 231 + name 232 + File.separator 233 + BACKUP_BUILD_REPORT_FILE_NAME 234 + name 235 + ") " 236 + lastModified 237 + ".txt";// NOI18N 238 } 239 240 public String getBackupBuildStatusDirectoryName() { 241 return OperationsXml.getFileLocation() 242 + OperationsXml.getOperationsDirectoryName() 243 + File.separator 244 + BUILD_STATUS_BACKUPS 245 + File.separator; 246 } 247 248 public String getBackupBuildStatusDirectoryName(String name) { 249 return getBackupBuildStatusDirectoryName() + name + File.separator; 250 } 251 252 /** 253 * Store the CSV train manifest 254 * 255 * @param name Full path name to CSV train manifest file. 256 * @return Train CSV manifest File. 257 */ 258 public File createTrainCsvManifestFile(String name) { 259 return createFile(getDefaultCsvManifestFileName(name)); // don't backup 260 } 261 262 public File getTrainCsvManifestFile(String name) { 263 File file = new File(getDefaultCsvManifestFileName(name)); 264 return file; 265 } 266 267 public String getDefaultCsvManifestFileName(String name) { 268 return getDefaultCsvManifestDirectory() + MANIFEST_FILE_NAME + name + FILE_TYPE_CSV; 269 } 270 271 private String getDefaultCsvManifestDirectory() { 272 return OperationsXml.getFileLocation() 273 + OperationsXml.getOperationsDirectoryName() 274 + File.separator 275 + CSV_MANIFESTS 276 + File.separator; 277 } 278 279 public void createDefaultCsvManifestDirectory() { 280 FileUtil.createDirectory(getDefaultCsvManifestDirectory()); 281 } 282 283 /** 284 * Store the Json manifest for a train 285 * 286 * @param name file name 287 * @param ext file extension to use 288 * @return Json manifest File 289 */ 290 public File createManifestFile(String name, String ext) { 291 return createFile(getDefaultManifestFileName(name, ext)); // don't backup 292 } 293 294 public File getManifestFile(String name, String ext) { 295 return new File(getDefaultManifestFileName(name, ext)); 296 } 297 298 private String getDefaultManifestFileName(String name, String ext) { 299 return InstanceManager.getDefault(OperationsManager.class).getPath(JSON_MANIFESTS) + File.separator + "train-" + name + "." + ext; // NOI18N 300 } 301 302 /** 303 * Store the switch list for a location 304 * 305 * @param name The location's name, to become file name. 306 * @return Switch list File. 307 */ 308 public File createSwitchListFile(String name) { 309 savePreviousSwitchListFile(name); 310 return createFile(getDefaultSwitchListName(name)); // don't backup 311 } 312 313 public File getSwitchListFile(String name) { 314 File file = new File(getDefaultSwitchListName(name)); 315 return file; 316 } 317 318 public String getDefaultSwitchListName(String name) { 319 return OperationsXml.getFileLocation() 320 + OperationsXml.getOperationsDirectoryName() 321 + File.separator 322 + SWITCH_LISTS 323 + File.separator 324 + SWITCH_LIST_FILE_NAME 325 + name 326 + FILE_TYPE_TXT; // NOI18N 327 } 328 329 /** 330 * Store the CSV switch list for a location 331 * 332 * @param name Location's name, to become file name. 333 * @return CSV switch list File. 334 */ 335 public File createCsvSwitchListFile(String name) { 336 return createFile(getDefaultCsvSwitchListFileName(name), true); // create backup 337 } 338 339 public File getCsvSwitchListFile(String name) { 340 File file = new File(getDefaultCsvSwitchListFileName(name)); 341 return file; 342 } 343 344 public String getDefaultCsvSwitchListFileName(String name) { 345 return getDefaultCsvSwitchListDirectoryName() + SWITCH_LIST_FILE_NAME + name + FILE_TYPE_CSV; 346 } 347 348 public String getDefaultCsvSwitchListDirectoryName() { 349 return OperationsXml.getFileLocation() 350 + OperationsXml.getOperationsDirectoryName() 351 + File.separator 352 + CSV_SWITCH_LISTS 353 + File.separator; 354 } 355 356 public void createDefaultCsvSwitchListDirectory() { 357 FileUtil.createDirectory(getDefaultCsvSwitchListDirectoryName()); 358 } 359 360 @Override 361 public void setOperationsFileName(String name) { 362 operationsFileName = name; 363 } 364 365 @Override 366 public String getOperationsFileName() { 367 return operationsFileName; 368 } 369 370 /** 371 * Save previous manifest file in a separate directory called 372 * manifestBackups. Each train manifest is saved in a unique directory using 373 * the train's name. 374 */ 375 private void savePreviousManifestFile(String name) { 376 if (Setup.isSaveTrainManifestsEnabled()) { 377 // create the manifest backup directory 378 createDirectory(getBackupManifestDirectoryName()); 379 // now create unique backup directory for each train manifest 380 createDirectory(getBackupManifestDirectoryName(name)); 381 // get old manifest file 382 File file = findFile(getDefaultManifestFileName(name)); 383 if (file == null) { 384 log.debug("No ({}) manifest file to backup", name); 385 } else if (file.canWrite()) { 386 String lastModified = new SimpleDateFormat("yyyyMMdd-HHmmssSSS").format(file.lastModified()); // NOI18N 387 String backupName = getBackupManifestFileName(name, lastModified); // NOI18N 388 if (file.renameTo(new File(backupName))) { 389 log.debug("created new manifest backup file {}", backupName); 390 } else { 391 log.error("could not create manifest backup file {}", backupName); 392 } 393 } 394 } 395 } 396 397 /** 398 * Save previous switch list file in a separate directory called 399 * switchListBackups. Each switch list is saved in a unique directory using 400 * the location's name. 401 */ 402 private void savePreviousSwitchListFile(String name) { 403 if (Setup.isSaveTrainManifestsEnabled()) { 404 // create the switch list backup directory 405 createDirectory(getBackupSwitchListDirectoryName()); 406 // now create unique backup directory for location 407 createDirectory(getBackupSwitchListDirectoryName(name)); 408 // get old switch list file 409 File file = findFile(getDefaultSwitchListName(name)); 410 if (file == null) { 411 log.debug("No ({}) switch list file to backup", name); 412 } else if (file.canRead()) { 413 String lastModified = new SimpleDateFormat("yyyyMMdd-HHmmssSSS").format(file.lastModified()); // NOI18N 414 String backupName = getBackupSwitchListFileName(name, lastModified); // NOI18N 415 File backupCopy = new File(backupName); 416 try { 417 FileUtil.copy(file, backupCopy); 418 log.debug("created new switch list backup file {}", backupName); 419 } catch (Exception e) { 420 log.error("could not create switch list backup file {}", backupName); 421 } 422 } 423 } 424 } 425 426 /** 427 * Save previous train build status file in a separate directory called 428 * BuildStatusBackups. Each build status is saved in a unique directory using 429 * the train's name. 430 * @param name train's name 431 */ 432 public void savePreviousBuildStatusFile(String name) { 433 if (Setup.isSaveTrainManifestsEnabled()) { 434 // create the build status backup directory 435 createDirectory(getBackupBuildStatusDirectoryName()); 436 // now create unique backup directory for each train 437 createDirectory(getBackupBuildStatusDirectoryName(name)); 438 // get old build status file for this train 439 File file = findFile(defaultBuildReportFileName(name)); 440 if (file == null) { 441 log.debug("No ({}) train build status file to backup", name); 442 } else if (file.canRead()) { 443 String lastModified = new SimpleDateFormat("yyyyMMdd-HHmmssSSS").format(file.lastModified()); // NOI18N 444 String backupName = getBackupBuildStatusFileName(name, lastModified); // NOI18N 445 File backupCopy = new File(backupName); 446 try { 447 FileUtil.copy(file, backupCopy); 448 log.debug("created new train build status backup file {}", backupName); 449 } catch (Exception e) { 450 log.error("could not create train build status backup file {}", backupName); 451 } 452 } 453 } 454 } 455 456 public void dispose() { 457 } 458 459 private final static Logger log = LoggerFactory.getLogger(TrainManagerXml.class); 460 461 @Override 462 public void initialize() { 463 load(); 464 } 465 466}