001package jmri.jmrit.dispatcher; 002 003import java.io.File; 004import java.io.IOException; 005import java.util.ArrayList; 006import java.util.Arrays; 007import java.util.List; 008import java.util.regex.Matcher; 009import java.util.regex.Pattern; 010import jmri.util.FileUtil; 011import jmri.util.XmlFilenameFilter; 012import org.jdom2.Document; 013import org.jdom2.Element; 014import org.slf4j.Logger; 015import org.slf4j.LoggerFactory; 016 017import jmri.InstanceManager; 018import jmri.configurexml.AbstractXmlAdapter.EnumIO; 019import jmri.configurexml.AbstractXmlAdapter.EnumIoNamesNumbers; 020import jmri.jmrit.dispatcher.ActiveTrain.TrainDetection; 021 022/** 023 * Handles reading and writing of TrainInfo files to disk as an XML file to/from 024 * the dispatcher/traininfo/ directory in the user's preferences area 025 * <p> 026 * This class manipulates the files conforming to the dispatcher-traininfo DTD 027 * <p> 028 * The file is written when the user requests that train information be saved. A 029 * TrainInfo file is read when the user request it in the Activate New Train 030 * window 031 * 032 * <p> 033 * This file is part of JMRI. 034 * <p> 035 * JMRI is open source software; you can redistribute it and/or modify it under 036 * the terms of version 2 of the GNU General Public License as published by the 037 * Free Software Foundation. See the "COPYING" file for a copy of this license. 038 * <p> 039 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY 040 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 041 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 042 * 043 * @author Dave Duchamp Copyright (C) 2009 044 */ 045public class TrainInfoFile extends jmri.jmrit.XmlFile { 046 047 public TrainInfoFile() { 048 super(); 049 } 050 // operational variables 051 private String fileLocation = FileUtil.getUserFilesPath() 052 + "dispatcher" + File.separator + "traininfo" + File.separator; 053 054 public void setFileLocation(String testLocation) { 055 fileLocation = testLocation; 056 } 057 private Document doc = null; 058 private Element root = null; 059 060 static final EnumIO<ActiveTrain.TrainDetection> trainsdectionFromEnumMap = new EnumIoNamesNumbers<>(ActiveTrain.TrainDetection.class); 061 static final EnumIO<ActiveTrain.TrainLengthUnits> trainlengthFromEnumMap = new EnumIoNamesNumbers<>(ActiveTrain.TrainLengthUnits.class); 062 063 /* 064 * Reads Dispatcher TrainInfo from a file in the user's preferences directory 065 * If the file containing Dispatcher TrainInfo does not exist this routine returns quietly. 066 * "name" is assumed to have the .xml or .XML extension already included 067 */ 068 public TrainInfo readTrainInfo(String name) throws org.jdom2.JDOMException, java.io.IOException { 069 log.debug("entered readTrainInfo for {}", name); 070 TrainInfo tInfo = null; 071 int version = 1; 072 // check if file exists 073 if (checkFile(fileLocation + name)) { 074 // file is present. 075 tInfo = new TrainInfo(); 076 root = rootFromName(fileLocation + name); 077 if (root != null) { 078 // there is a file 079 Element traininfo = root.getChild("traininfo"); 080 if (traininfo != null) { 081 // get version so we dont look for missing fields 082 if (traininfo.getAttribute("version") != null ) { 083 try { 084 version = traininfo.getAttribute("version").getIntValue(); 085 } 086 catch(Exception ex) { 087 log.error("Traininfo file version number not an integer: assuming version 1"); 088 version = 1; 089 } 090 } else { 091 version = 1; 092 } 093 tInfo.setVersion(version); 094 // there are train info options defined, read them 095 if (traininfo.getAttribute("transitname") != null) { 096 // there is a transit name selected 097 tInfo.setTransitName(traininfo.getAttribute("transitname").getValue()); 098 } else { 099 log.error("Transit name missing when reading TrainInfoFile {}", name); 100 } 101 if (version < 6) { 102 if (traininfo.getAttribute("trainname") != null) { 103 tInfo.setTrainName(traininfo.getAttribute("trainname").getValue()); 104 tInfo.setRosterId(traininfo.getAttribute("trainname").getValue()); 105 tInfo.setTrainUserName(traininfo.getAttribute("trainname").getValue()); 106 } else { 107 log.error("Train name missing when reading TrainInfoFile {}", name); 108 } 109 } else { 110 if (traininfo.getAttribute("trainname") != null) { 111 tInfo.setRosterId(traininfo.getAttribute("trainname").getValue()); 112 } 113 if (traininfo.getAttribute("rosterid") != null) { 114 tInfo.setRosterId(traininfo.getAttribute("rosterid").getValue()); 115 } 116 if (traininfo.getAttribute("trainusername") != null) { 117 tInfo.setTrainUserName(traininfo.getAttribute("trainusername").getValue()); 118 } 119 } 120 if (traininfo.getAttribute("dccaddress") != null) { 121 tInfo.setDccAddress(traininfo.getAttribute("dccaddress").getValue()); 122 } else { 123 log.error("DCC Address missing when reading TrainInfoFile {}", name); 124 } 125 if (traininfo.getAttribute("trainintransit") != null) { 126 tInfo.setTrainInTransit(true); 127 if (traininfo.getAttribute("trainintransit").getValue().equals("no")) { 128 tInfo.setTrainInTransit(false); 129 } 130 } else { 131 log.error("Train in Transit check box missing when reading TrainInfoFile {}", name); 132 } 133 if (traininfo.getAttribute("startblockname") != null) { 134 // there is a transit name selected 135 tInfo.setStartBlockName(traininfo.getAttribute("startblockname").getValue()); 136 } else { 137 log.error("Start block name missing when reading TrainInfoFile {}", name); 138 } 139 if (traininfo.getAttribute("endblockname") != null) { 140 // there is a transit name selected 141 tInfo.setDestinationBlockName(traininfo.getAttribute("endblockname").getValue()); 142 } else { 143 log.error("Destination block name missing when reading TrainInfoFile {}", name); 144 } 145 146 if (traininfo.getAttribute("trainfromroster") != null) { 147 tInfo.setTrainFromRoster(true); 148 if (traininfo.getAttribute("trainfromroster").getValue().equals("no")) { 149 tInfo.setTrainFromRoster(false); 150 } 151 } 152 if (traininfo.getAttribute("trainfromtrains") != null) { 153 tInfo.setTrainFromTrains(true); 154 if (traininfo.getAttribute("trainfromtrains").getValue().equals("no")) { 155 tInfo.setTrainFromTrains(false); 156 } 157 } 158 if (traininfo.getAttribute("trainfromuser") != null) { 159 tInfo.setTrainFromUser(true); 160 if (traininfo.getAttribute("trainfromuser").getValue().equals("no")) { 161 tInfo.setTrainFromUser(false); 162 } 163 } 164 if (traininfo.getAttribute("trainfromsetlater") != null) { 165 tInfo.setTrainFromSetLater(true); 166 if (traininfo.getAttribute("trainfromsetlater").getValue().equals("no")) { 167 tInfo.setTrainFromSetLater(false); 168 } 169 } 170 if (traininfo.getAttribute("priority") != null) { 171 tInfo.setPriority(Integer.parseInt(traininfo.getAttribute("priority").getValue())); 172 } else { 173 log.error("Priority missing when reading TrainInfoFile {}", name); 174 } 175 if (traininfo.getAttribute("allocatealltheway") != null) { 176 if (traininfo.getAttribute("allocatealltheway").getValue().equals("yes")) { 177 tInfo.setAllocateAllTheWay(true); 178 } 179 } 180 if (traininfo.getAttribute("allocationmethod") != null) { 181 tInfo.setAllocationMethod(traininfo.getAttribute("allocationmethod").getIntValue()); 182 } 183 if (traininfo.getAttribute("nexttrain") != null) { 184 tInfo.setNextTrain(traininfo.getAttribute("nexttrain").getValue()); 185 } 186 if (traininfo.getAttribute("resetwhendone") != null) { 187 if (traininfo.getAttribute("resetwhendone").getValue().equals("yes")) { 188 tInfo.setResetWhenDone(true); 189 } 190 if (traininfo.getAttribute("delayedrestart") != null) { 191 // for older files that didnot have seperate restart details for to and fro 192 // we default that data to this data. 193 switch (traininfo.getAttribute("delayedrestart").getValue()) { 194 case "no": 195 tInfo.setDelayedRestart(ActiveTrain.NODELAY); 196 tInfo.setReverseDelayedRestart(ActiveTrain.NODELAY); 197 break; 198 case "sensor": 199 tInfo.setDelayedRestart(ActiveTrain.SENSORDELAY); 200 tInfo.setReverseDelayedRestart(ActiveTrain.SENSORDELAY); 201 if (traininfo.getAttribute("delayedrestartsensor") != null) { 202 tInfo.setRestartSensorName(traininfo.getAttribute("delayedrestartsensor").getValue()); 203 tInfo.setReverseRestartSensorName(traininfo.getAttribute("delayedrestartsensor").getValue()); 204 } 205 if (traininfo.getAttribute("resetrestartsensor") != null) { 206 tInfo.setResetRestartSensor(traininfo.getAttribute("resetrestartsensor").getValue().equals("yes")); 207 tInfo.setReverseResetRestartSensor(traininfo.getAttribute("resetrestartsensor").getValue().equals("yes")); 208 } 209 break; 210 case "timed": 211 tInfo.setDelayedRestart(ActiveTrain.TIMEDDELAY); 212 tInfo.setReverseDelayedRestart(ActiveTrain.TIMEDDELAY); 213 if (traininfo.getAttribute("delayedrestarttime") != null) { 214 tInfo.setRestartDelayMin((int) traininfo.getAttribute("delayedrestarttime").getLongValue()); 215 tInfo.setReverseRestartDelayMin((int) traininfo.getAttribute("delayedrestarttime").getLongValue()); 216 } break; 217 default: 218 break; 219 } 220 } 221 } 222 if (traininfo.getAttribute("reverseatend") != null) { 223 tInfo.setReverseAtEnd(true); 224 if (traininfo.getAttribute("reverseatend").getValue().equals("no")) { 225 tInfo.setReverseAtEnd(false); 226 } 227 if (version > 3) { 228 // fro delays are independent from to delays 229 if (traininfo.getAttribute("reversedelayedrestart") != null) { 230 switch (traininfo.getAttribute("reversedelayedrestart").getValue()) { 231 case "no": 232 tInfo.setReverseDelayedRestart(ActiveTrain.NODELAY); 233 break; 234 case "sensor": 235 tInfo.setReverseDelayedRestart(ActiveTrain.SENSORDELAY); 236 if (traininfo.getAttribute("reversedelayedrestartsensor") != null) { 237 tInfo.setReverseRestartSensorName( 238 traininfo.getAttribute("reversedelayedrestartsensor").getValue()); 239 } 240 if (traininfo.getAttribute("reverseresetrestartsensor") != null) { 241 tInfo.setReverseResetRestartSensor( 242 traininfo.getAttribute("reverseresetrestartsensor").getValue() 243 .equals("yes")); 244 } 245 break; 246 case "timed": 247 tInfo.setReverseDelayedRestart(ActiveTrain.TIMEDDELAY); 248 if (traininfo.getAttribute("reversedelayedrestarttime") != null) { 249 tInfo.setReverseRestartDelayMin((int) traininfo 250 .getAttribute("reversedelayedrestarttime").getLongValue()); 251 } 252 break; 253 default: 254 break; 255 } 256 } 257 } 258 } 259 if (traininfo.getAttribute("delayedstart") != null) { 260 switch (traininfo.getAttribute("delayedstart").getValue()) { 261 case "no": 262 tInfo.setDelayedStart(ActiveTrain.NODELAY); 263 break; 264 case "sensor": 265 tInfo.setDelayedStart(ActiveTrain.SENSORDELAY); 266 break; 267 default: 268 //This covers the old versions of the file with "yes" 269 tInfo.setDelayedStart(ActiveTrain.TIMEDDELAY); 270 break; 271 } 272 } 273 if (traininfo.getAttribute("departuretimehr") != null) { 274 tInfo.setDepartureTimeHr(Integer.parseInt(traininfo.getAttribute("departuretimehr").getValue())); 275 } 276 if (traininfo.getAttribute("departuretimemin") != null) { 277 tInfo.setDepartureTimeMin(Integer.parseInt(traininfo.getAttribute("departuretimemin").getValue())); 278 } 279 if (traininfo.getAttribute("delayedSensor") != null) { 280 tInfo.setDelaySensorName(traininfo.getAttribute("delayedSensor").getValue()); 281 } 282 if (traininfo.getAttribute("resetstartsensor") != null) { 283 tInfo.setResetStartSensor(traininfo.getAttribute("resetstartsensor").getValue().equals("yes")); 284 } 285 if (traininfo.getAttribute("traintype") != null) { 286 tInfo.setTrainType(traininfo.getAttribute("traintype").getValue()); 287 } 288 if (traininfo.getAttribute("autorun") != null) { 289 tInfo.setAutoRun(true); 290 if (traininfo.getAttribute("autorun").getValue().equals("no")) { 291 tInfo.setAutoRun(false); 292 } 293 } 294 if (traininfo.getAttribute("loadatstartup") != null) { 295 tInfo.setLoadAtStartup(true); 296 if (traininfo.getAttribute("loadatstartup").getValue().equals("no")) { 297 tInfo.setLoadAtStartup(false); 298 } 299 } 300 // here retrieve items related only to automatically run trains if present 301 if (traininfo.getAttribute("speedfactor") != null) { 302 tInfo.setSpeedFactor(Float.parseFloat(traininfo.getAttribute("speedfactor").getValue())); 303 } 304 if (traininfo.getAttribute("maxspeed") != null) { 305 tInfo.setMaxSpeed(Float.parseFloat(traininfo.getAttribute("maxspeed").getValue())); 306 } 307 if (traininfo.getAttribute("minreliableoperatingspeed") != null) { 308 tInfo.setMinReliableOperatingSpeed(Float.parseFloat(traininfo.getAttribute("minreliableoperatingspeed").getValue())); 309 } 310 if (traininfo.getAttribute("ramprate") != null) { 311 tInfo.setRampRate(traininfo.getAttribute("ramprate").getValue()); 312 } 313 tInfo.setTrainDetection(TrainDetection.TRAINDETECTION_WHOLETRAIN); 314 if (version > 4) { 315 if (traininfo.getAttribute("traindetection") != null) { 316 tInfo.setTrainDetection(trainsdectionFromEnumMap.inputFromAttribute(traininfo.getAttribute("traindetection"))); 317 } 318 } 319 else { 320 if (traininfo.getAttribute("resistancewheels").getValue().equals("no")) { 321 tInfo.setTrainDetection(TrainDetection.TRAINDETECTION_HEADONLY); 322 } 323 } 324 if (traininfo.getAttribute("runinreverse") != null) { 325 tInfo.setRunInReverse(true); 326 if (traininfo.getAttribute("runinreverse").getValue().equals("no")) { 327 tInfo.setRunInReverse(false); 328 } 329 } 330 if (traininfo.getAttribute("sounddecoder") != null) { 331 tInfo.setSoundDecoder(true); 332 if (traininfo.getAttribute("sounddecoder").getValue().equals("no")) { 333 tInfo.setSoundDecoder(false); 334 } 335 } 336 if (version > 5) { 337 if (traininfo.getAttribute("trainlengthunits") != null) { 338 tInfo.setTrainLengthUnits(trainlengthFromEnumMap.inputFromAttribute(traininfo.getAttribute("trainlengthunits"))); 339 } 340 } 341 if (traininfo.getAttribute("maxtrainlengthMeters") != null) { 342 tInfo.setMaxTrainLengthScaleMeters(Float.parseFloat(traininfo.getAttribute("maxtrainlengthscalemeters").getValue())); 343 } else { 344 if (traininfo.getAttribute("maxtrainlength") != null) { 345 if (InstanceManager.getDefault(DispatcherFrame.class).getUseScaleMeters()) { 346 tInfo.setMaxTrainLengthScaleMeters(Float.parseFloat(traininfo.getAttribute("maxtrainlength").getValue())); 347 } else { 348 tInfo.setMaxTrainLengthScaleFeet(Float.parseFloat(traininfo.getAttribute("maxtrainlength").getValue())); 349 } 350 } 351 } 352 if (traininfo.getAttribute("terminatewhendone") != null) { 353 tInfo.setTerminateWhenDone(false); 354 if (traininfo.getAttribute("terminatewhendone").getValue().equals("yes")) { 355 tInfo.setTerminateWhenDone(true); 356 } 357 } 358 if (traininfo.getAttribute("usespeedprofile") != null) { 359 tInfo.setUseSpeedProfile(false); 360 if (traininfo.getAttribute("usespeedprofile").getValue().equals("yes")) { 361 tInfo.setUseSpeedProfile(true); 362 } 363 } 364 if (traininfo.getAttribute("stopbyspeedprofile") != null) { 365 tInfo.setStopBySpeedProfile(false); 366 if (traininfo.getAttribute("stopbyspeedprofile").getValue().equals("yes")) { 367 tInfo.setStopBySpeedProfile(true); 368 } 369 } 370 if (traininfo.getAttribute("stopbyspeedprofileadjust") != null) { 371 tInfo.setStopBySpeedProfileAdjust(traininfo.getAttribute("stopbyspeedprofileadjust").getFloatValue()); 372 } 373 if (traininfo.getAttribute("waittime") != null) { 374 tInfo.setWaitTime(traininfo.getAttribute("waittime").getFloatValue()); 375 } 376 if (traininfo.getAttribute("blockname") != null) { 377 tInfo.setBlockName(traininfo.getAttribute("blockname").getValue()); 378 } 379 380 if (version == 1) { 381 String parseArray[]; 382 // If you only have a systemname then its everything before the dash 383 tInfo.setStartBlockId(tInfo.getStartBlockName().split("-")[0]); 384 // If you have a systemname and username you want everything before the open bracket 385 tInfo.setStartBlockId(tInfo.getStartBlockId().split("\\(")[0]); 386 // to guard against a dash in the names, we need the last part, not just [1] 387 parseArray = tInfo.getStartBlockName().split("-"); 388 tInfo.setStartBlockSeq(-1); // default value 389 if (parseArray.length > 0) { 390 try { 391 tInfo.setStartBlockSeq(Integer.parseInt(parseArray[parseArray.length -1])); 392 } 393 catch (Exception Ex) { 394 log.error("Invalid StartBlockSequence{}",parseArray[parseArray.length -1]); 395 } 396 } 397 // repeat for destination 398 tInfo.setDestinationBlockId(tInfo.getDestinationBlockName().split("-")[0]); 399 tInfo.setDestinationBlockId(tInfo.getDestinationBlockId().split("\\(")[0]); 400 parseArray = tInfo.getDestinationBlockName().split("-"); 401 tInfo.setDestinationBlockSeq(-1); 402 if (parseArray.length > 0) { 403 try { 404 tInfo.setDestinationBlockSeq(Integer.parseInt(parseArray[parseArray.length -1])); 405 } 406 catch (Exception Ex) { 407 log.error("Invalid StartBlockSequence{}",parseArray[parseArray.length -1]); 408 } 409 } 410 // Transit we need the whole thing or the bit before the first open bracket 411 tInfo.setTransitId(tInfo.getTransitName().split("\\(")[0]); 412 log.debug("v1: t = {}, bs = {}, be = {}", tInfo.getTransitName(), tInfo.getStartBlockName(), tInfo.getDestinationBlockName()); 413 } 414 if ( version > 1 ) { 415 if (traininfo.getAttribute("transitid") != null) { 416 // there is a transit name selected 417 tInfo.setTransitId(traininfo.getAttribute("transitid").getValue()); 418 } else { 419 log.error("Transit id missing when reading TrainInfoFile {}", name); 420 } 421 if (traininfo.getAttribute("startblockid") != null) { 422 // there is a transit name selected 423 tInfo.setStartBlockId(traininfo.getAttribute("startblockid").getValue()); 424 } else { 425 log.error("Start block Id missing when reading TrainInfoFile {}", name); 426 } 427 if (traininfo.getAttribute("endblockid") != null) { 428 // there is a transit name selected 429 tInfo.setDestinationBlockId(traininfo.getAttribute("endblockid").getValue()); 430 } else { 431 log.error("Destination block Id missing when reading TrainInfoFile {}", name); 432 } 433 if (traininfo.getAttribute("startblockseq") != null) { 434 // there is a transit name selected 435 try { 436 tInfo.setStartBlockSeq(traininfo.getAttribute("startblockseq").getIntValue()); 437 } 438 catch (Exception ex) { 439 log.error("Start block sequence invalid when reading TrainInfoFile"); 440 } 441 } else { 442 log.error("Start block sequence missing when reading TrainInfoFile {}", name); 443 } 444 if (traininfo.getAttribute("endblockseq") != null) { 445 // there is a transit name selected 446 try { 447 tInfo.setDestinationBlockSeq(traininfo.getAttribute("endblockseq").getIntValue()); 448 } 449 catch (Exception ex) { 450 log.error("Destination block sequence invalid when reading TrainInfoFile {}", name); 451 } 452 } else { 453 log.error("Destination block sequence missing when reading TrainInfoFile {}", name); 454 } 455 } 456 if ( version == 1 || version == 2) { 457 // Change transit and block names from sysName(userName) to displayName 458 tInfo.setTransitName(convertName(tInfo.getTransitName())); 459 tInfo.setStartBlockName(convertName(tInfo.getStartBlockName())); 460 tInfo.setDestinationBlockName(convertName(tInfo.getDestinationBlockName())); 461 } 462 } 463 } 464 } 465 return tInfo; 466 } 467 468 public String convertName(String name) { 469 // transit: sys(user), block: sys(user)-n 470 String newName = name; 471 472 Pattern p = Pattern.compile(".+\\((.+)\\)(-\\d+)*"); 473 Matcher m = p.matcher(name); 474 if (m.matches()) { 475 log.debug("regex: name = '{}', group 1 = '{}', group 2 = '{}'", name, m.group(1), m.group(2)); 476 if (m.group(1) != null) { 477 newName = m.group(1).trim(); 478 if (m.group(2) != null) { 479 newName = newName + m.group(2).trim(); 480 } 481 } 482 } 483 484 log.debug("convertName: old = '{}', new = '{}'", name, newName); 485 return newName; 486 } 487 488 /* 489 * Writes out Dispatcher options to a file in the user's preferences directory 490 */ 491 public void writeTrainInfo(TrainInfo tf, String name) throws java.io.IOException { 492 log.debug("entered writeTrainInfo"); 493 root = new Element("traininfofile"); 494 doc = newDocument(root, dtdLocation + "dispatcher-traininfo.dtd"); 495 // add XSLT processing instruction 496 // <?xml-stylesheet type="text/xsl" href="XSLT/block-values.xsl"?> 497 java.util.Map<String, String> m = new java.util.HashMap<>(); 498 m.put("type", "text/xsl"); 499 m.put("href", xsltLocation + "dispatcher-traininfo.xsl"); 500 org.jdom2.ProcessingInstruction p = new org.jdom2.ProcessingInstruction("xml-stylesheet", m); 501 doc.addContent(0, p); 502 503 // save Dispatcher TrainInfo in xml format 504 Element traininfo = new Element("traininfo"); 505 // write version number 506 traininfo.setAttribute("version", "7"); 507 traininfo.setAttribute("transitname", tf.getTransitName()); 508 traininfo.setAttribute("transitid", tf.getTransitId()); 509 traininfo.setAttribute("trainname", tf.getTrainName()); 510 traininfo.setAttribute("trainusername", tf.getTrainUserName()); 511 traininfo.setAttribute("rosterid", tf.getRosterId()); 512 traininfo.setAttribute("dccaddress", tf.getDccAddress()); 513 traininfo.setAttribute("trainintransit", "" + (tf.getTrainInTransit() ? "yes" : "no")); 514 traininfo.setAttribute("startblockname", tf.getStartBlockName()); 515 traininfo.setAttribute("startblockid", tf.getStartBlockId()); 516 traininfo.setAttribute("startblockseq", Integer.toString(tf.getStartBlockSeq())); 517 traininfo.setAttribute("endblockname", tf.getDestinationBlockName()); 518 traininfo.setAttribute("endblockid", tf.getDestinationBlockId()); 519 traininfo.setAttribute("endblockseq", Integer.toString(tf.getDestinationBlockSeq())); 520 traininfo.setAttribute("trainfromroster", "" + (tf.getTrainFromRoster() ? "yes" : "no")); 521 traininfo.setAttribute("trainfromtrains", "" + (tf.getTrainFromTrains() ? "yes" : "no")); 522 traininfo.setAttribute("trainfromuser", "" + (tf.getTrainFromUser() ? "yes" : "no")); 523 traininfo.setAttribute("trainfromsetlater", "" + (tf.getTrainFromSetLater() ? "yes" : "no")); 524 traininfo.setAttribute("priority", Integer.toString(tf.getPriority())); 525 traininfo.setAttribute("traindetection", trainsdectionFromEnumMap.outputFromEnum(tf.getTrainDetection())); 526 traininfo.setAttribute("resetwhendone", "" + (tf.getResetWhenDone() ? "yes" : "no")); 527 switch (tf.getDelayedRestart()) { 528 case ActiveTrain.SENSORDELAY: 529 traininfo.setAttribute("delayedrestart", "sensor"); 530 traininfo.setAttribute("delayedrestartsensor", tf.getRestartSensorName()); 531 traininfo.setAttribute("resetrestartsensor", "" + (tf.getResetRestartSensor() ? "yes" : "no")); 532 break; 533 case ActiveTrain.TIMEDDELAY: 534 traininfo.setAttribute("delayedrestart", "timed"); 535 traininfo.setAttribute("delayedrestarttime", Integer.toString(tf.getRestartDelayMin())); 536 break; 537 default: 538 traininfo.setAttribute("delayedrestart", "no"); 539 break; 540 } 541 542 traininfo.setAttribute("reverseatend", "" + (tf.getReverseAtEnd() ? "yes" : "no")); 543 switch (tf.getReverseDelayedRestart()) { 544 case ActiveTrain.SENSORDELAY: 545 traininfo.setAttribute("reversedelayedrestart", "sensor"); 546 traininfo.setAttribute("reversedelayedrestartsensor", tf.getReverseRestartSensorName()); 547 traininfo.setAttribute("reverseresetrestartsensor", "" + (tf.getReverseResetRestartSensor() ? "yes" : "no")); 548 break; 549 case ActiveTrain.TIMEDDELAY: 550 traininfo.setAttribute("reversedelayedrestart", "timed"); 551 traininfo.setAttribute("reversedelayedrestarttime", Integer.toString(tf.getReverseRestartDelayMin())); 552 break; 553 default: 554 traininfo.setAttribute("reversedelayedrestart", "no"); 555 break; 556 } 557 if (tf.getDelayedStart() == ActiveTrain.TIMEDDELAY) { 558 traininfo.setAttribute("delayedstart", "timed"); 559 } else if (tf.getDelayedStart() == ActiveTrain.SENSORDELAY) { 560 traininfo.setAttribute("delayedstart", "sensor"); 561 if (tf.getDelaySensorName() != null) { 562 traininfo.setAttribute("delayedSensor", tf.getDelaySensorName()); 563 traininfo.setAttribute("resetstartsensor", "" + (tf.getResetStartSensor() ? "yes" : "no")); 564 } 565 } 566 567 traininfo.setAttribute("terminatewhendone", (tf.getTerminateWhenDone() ? "yes" : "no")); 568 traininfo.setAttribute("departuretimehr", Integer.toString(tf.getDepartureTimeHr())); 569 traininfo.setAttribute("departuretimemin", Integer.toString(tf.getDepartureTimeMin())); 570 traininfo.setAttribute("traintype", tf.getTrainType()); 571 traininfo.setAttribute("autorun", "" + (tf.getAutoRun() ? "yes" : "no")); 572 traininfo.setAttribute("loadatstartup", "" + (tf.getLoadAtStartup() ? "yes" : "no")); 573 traininfo.setAttribute("allocatealltheway", "" + (tf.getAllocateAllTheWay() ? "yes" : "no")); 574 traininfo.setAttribute("allocationmethod", Integer.toString(tf.getAllocationMethod())); 575 traininfo.setAttribute("nexttrain", tf.getNextTrain()); 576 // here save items related to automatically running active trains 577 traininfo.setAttribute("speedfactor", Float.toString(tf.getSpeedFactor())); 578 traininfo.setAttribute("maxspeed", Float.toString(tf.getMaxSpeed())); 579 traininfo.setAttribute("minreliableoperatingspeed", Float.toString(tf.getMinReliableOperatingSpeed())); 580 traininfo.setAttribute("ramprate", tf.getRampRate()); 581 traininfo.setAttribute("runinreverse", "" + (tf.getRunInReverse() ? "yes" : "no")); 582 traininfo.setAttribute("sounddecoder", "" + (tf.getSoundDecoder() ? "yes" : "no")); 583 traininfo.setAttribute("maxtrainlengthscalemeters", Float.toString(tf.getMaxTrainLengthScaleMeters())); 584 traininfo.setAttribute("trainlengthunits", trainlengthFromEnumMap.outputFromEnum(tf.getTrainLengthUnits())); 585 traininfo.setAttribute("usespeedprofile", "" + (tf.getUseSpeedProfile() ? "yes" : "no")); 586 traininfo.setAttribute("stopbyspeedprofile", "" + (tf.getStopBySpeedProfile() ? "yes" : "no")); 587 traininfo.setAttribute("stopbyspeedprofileadjust", Float.toString(tf.getStopBySpeedProfileAdjust())); 588 traininfo.setAttribute("waittime", Float.toString(tf.getWaitTime())); 589 traininfo.setAttribute("blockname", tf.getBlockName()); 590 591 root.addContent(traininfo); 592 593 // write out the file 594 try { 595 if (!checkFile(fileLocation + name)) { 596 // file does not exist, create it 597 File file = new File(fileLocation + name); 598 if (!file.createNewFile()) // create file and check result 599 { 600 log.error("createNewFile failed"); 601 } 602 } 603 // write content to file 604 writeXML(findFile(fileLocation + name), doc); 605 } catch (java.io.IOException ioe) { 606 log.error("IO Exception writing", ioe); 607 throw (ioe); 608 } 609 } 610 611 /** 612 * Get the names of all current TrainInfo files. Returns names as an array 613 * of Strings. Returns an empty array if no files are present. Note: Fill 614 * names still end with .xml or .XML. (Modeled after a method in 615 * RecreateRosterAction.java by Bob Jacobsen) 616 * 617 * @return names as an array or an empty array if none present 618 */ 619 public String[] getTrainInfoFileNames() { 620 // ensure preferences will be found for read 621 FileUtil.createDirectory(fileLocation); 622 // create an array of file names from roster dir in preferences, count entries 623 List<String> names = new ArrayList<>(); 624 log.debug("directory of TrainInfoFiles is {}", fileLocation); 625 File fp = new File(fileLocation); 626 if (fp.exists()) { 627 String[] xmlList = fp.list(new XmlFilenameFilter()); 628 if (xmlList!=null) { 629 names.addAll(Arrays.asList(xmlList)); 630 } 631 } 632 // Sort the resulting array 633 names.sort((s1, s2) -> { 634 return s1.compareTo(s2); 635 }); 636 return names.toArray(new String[names.size()]); 637 } 638 639 /** 640 * Get the names of all current TrainInfo files. Returns list 641 * of files and some basic details for each file 642 *. 643 * @return names as an array or an empty array if none present 644 */ 645 public List<TrainInfoFileSummary> getTrainInfoFileSummaries() { 646 List<TrainInfoFileSummary> summaries = new ArrayList<>(); 647 for (String fileName : getTrainInfoFileNames()) { 648 try { 649 TrainInfo ti = readTrainInfo(fileName); 650 summaries.add(new TrainInfoFileSummary(fileName, ti.getTransitName(), ti.getTrainName(), 651 ti.getStartBlockName(), ti.getDestinationBlockName(), ti.getDccAddress())); 652 } catch (org.jdom2.JDOMException ex) { 653 summaries.add(new TrainInfoFileSummary(fileName)); 654 } catch (IOException ex) { 655 summaries.add(new TrainInfoFileSummary(fileName)); 656 } 657 } 658 return summaries; 659 } 660 661 /** 662 * Delete a specified TrainInfo file. 663 * 664 * @param name the file to delete 665 */ 666 public void deleteTrainInfoFile(String name) { 667 // locate the file and delete it if it exists 668 File f = new File(fileLocation + name); 669 if (!f.delete()) { // delete file and check success 670 log.error("failed to delete TrainInfo file - {}", name); 671 } 672 } 673 674 private final static Logger log = LoggerFactory.getLogger(TrainInfoFile.class); 675}