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