001package jmri.jmrit.operations.automation; 002 003import java.util.ArrayList; 004import java.util.List; 005 006import javax.swing.JComboBox; 007 008import org.jdom2.Element; 009import org.slf4j.Logger; 010import org.slf4j.LoggerFactory; 011 012import jmri.InstanceManager; 013import jmri.beans.PropertyChangeSupport; 014import jmri.jmrit.operations.automation.actions.*; 015import jmri.jmrit.operations.routes.RouteLocation; 016import jmri.jmrit.operations.setup.Control; 017import jmri.jmrit.operations.trains.Train; 018import jmri.jmrit.operations.trains.TrainManager; 019import jmri.jmrit.operations.trains.TrainManagerXml; 020import jmri.jmrit.operations.trains.schedules.TrainSchedule; 021import jmri.jmrit.operations.trains.schedules.TrainScheduleManager; 022 023/** 024 * Represents one automation item of a automation 025 * 026 * @author Daniel Boudreau Copyright (C) 2016 027 */ 028public class AutomationItem extends PropertyChangeSupport implements java.beans.PropertyChangeListener { 029 030 public static final String NONE = ""; // NOI18N 031 032 protected String _id = NONE; 033 protected int _sequenceId = 0; // used to determine order in automation 034 035 protected boolean _actionRunning = false; // when true action is running, for example waiting for a train 036 protected boolean _actionSuccessful = false; 037 protected boolean _actionRan = false; 038 protected boolean _haltFail = true; 039 040 protected Action _action = null; 041 protected String _message = NONE; 042 protected String _messageFail = NONE; 043 044 // the following are associated with actions 045 protected Train _train = null; 046 protected RouteLocation _routeLocation = null; 047 protected String _automationIdToRun = NONE; 048 protected String _gotoAutomationItemId = NONE; // the goto automationItem 049 protected boolean _gotoAutomationBranched = false; 050 protected String _trainScheduleId = NONE; 051 052 public static final String DISPOSE = "automationItemDispose"; // NOI18N 053 054 public AutomationItem(String id) { 055 log.debug("New automation item id: {}", id); 056 _id = id; 057 setAction(new NoAction()); // the default 058 } 059 060 public String getId() { 061 return _id; 062 } 063 064 @Override 065 public String toString() { 066 return getId(); // for property changes 067 } 068 069 public int getSequenceId() { 070 return _sequenceId; 071 } 072 073 public void setSequenceId(int sequence) { 074 // property change not needed 075 _sequenceId = sequence; 076 } 077 078 public void setAction(Action action) { 079 Action old = _action; 080 _action = action; 081 if (old != null) { 082 old.cancelAction(); 083 } 084 if (action != null) { 085 action.setAutomationItem(this); // associate action with this item 086 } 087 if (old != action) { 088 setDirtyAndFirePropertyChange("AutomationItemActionChange", old, action); // NOI18N 089 } 090 } 091 092 public Action getAction() { 093 return _action; 094 } 095 096 public String getActionName() { 097 if (getAction() != null) { 098 return getAction().getName(); 099 } 100 return NONE; 101 } 102 103 public int getActionCode() { 104 if (getAction() != null) { 105 return getAction().getCode(); 106 } 107 return ActionCodes.NO_ACTION; 108 } 109 110 public void doAction() { 111 if (getAction() != null) { 112 getAction().doAction(); 113 } 114 } 115 116 public void setTrain(Train train) { 117 Train old = _train; 118 _train = train; 119 if (old != train) { 120 setDirtyAndFirePropertyChange("AutomationItemTrainChange", old, train); // NOI18N 121 setRouteLocation(null); 122 } 123 } 124 125 public Train getTrain() { 126 if (getAction() != null && getAction().isTrainMenuEnabled()) { 127 return _train; 128 } 129 return null; 130 } 131 132 public void setRouteLocation(RouteLocation rl) { 133 RouteLocation old = _routeLocation; 134 _routeLocation = rl; 135 if (old != rl) { 136 setDirtyAndFirePropertyChange("AutomationItemRouteLocationChange", old, rl); // NOI18N 137 } 138 } 139 140 public RouteLocation getRouteLocation() { 141 if (getAction() != null && getAction().isRouteMenuEnabled()) { 142 return _routeLocation; 143 } 144 return null; 145 } 146 147 public void setOther(Object other) { 148 if (other != null && other.getClass().equals(Automation.class)) { 149 setAutomationToRun((Automation) other); 150 } 151 else if (other != null && other.getClass().equals(AutomationItem.class)) { 152 setGotoAutomationItem((AutomationItem) other); 153 } 154 else if (other == null || other.getClass().equals(TrainSchedule.class)) { 155 setTrainSchedule((TrainSchedule) other); 156 } 157 } 158 159 /** 160 * The automation for actions, not the automation associated with this item. 161 * @param automation the automation to run 162 * 163 */ 164 public void setAutomationToRun(Automation automation) { 165 Automation old = InstanceManager.getDefault(AutomationManager.class).getAutomationById(_automationIdToRun); 166 if (automation != null) 167 _automationIdToRun = automation.getId(); 168 else 169 _automationIdToRun = NONE; 170 if (old != automation) { 171 setDirtyAndFirePropertyChange("AutomationItemAutomationChange", old, automation); // NOI18N 172 } 173 } 174 175 /** 176 * The automation for actions, not the automation associated with this item. 177 * 178 * @return Automation for this action 179 */ 180 public Automation getAutomationToRun() { 181 if (getAction() != null && getAction().isAutomationMenuEnabled()) { 182 return InstanceManager.getDefault(AutomationManager.class).getAutomationById(_automationIdToRun); 183 } 184 return null; 185 } 186 187 /** 188 * The automation for action GOTO, not this automation item. 189 * @param automationItem which automation item to GOTO 190 * 191 */ 192 public void setGotoAutomationItem(AutomationItem automationItem) { 193 AutomationItem oldItem = null; 194 if (automationItem != null) { 195 Automation automation = InstanceManager.getDefault(AutomationManager.class).getAutomationById(automationItem.getId().split(Automation.REGEX)[0]); 196 oldItem = automation.getItemById(_gotoAutomationItemId); 197 _gotoAutomationItemId = automationItem.getId(); 198 } else { 199 _gotoAutomationItemId = NONE; 200 } 201 if (oldItem != automationItem) { 202 setDirtyAndFirePropertyChange("AutomationItemAutomationChange", oldItem, automationItem); // NOI18N 203 } 204 } 205 206 /** 207 * The automationItem for actions not this item. 208 * 209 * @return AutomationItem for GOTO 210 */ 211 public AutomationItem getGotoAutomationItem() { 212 if (getAction() != null && getAction().isGotoMenuEnabled()) { 213 Automation automation = InstanceManager.getDefault(AutomationManager.class).getAutomationById(_gotoAutomationItemId.split(Automation.REGEX)[0]); 214 if (automation != null) { 215 return automation.getItemById(_gotoAutomationItemId); 216 } 217 } 218 return null; 219 } 220 221 public void setGotoBranched(boolean branched) { 222 _gotoAutomationBranched = branched; 223 } 224 225 public boolean isGotoBranched() { 226 return _gotoAutomationBranched; 227 } 228 229 public void setTrainSchedule(TrainSchedule trainSchedule) { 230 String old = _trainScheduleId; 231 if (trainSchedule != null) { 232 _trainScheduleId = trainSchedule.getId(); 233 } else { 234 _trainScheduleId = NONE; 235 } 236 if (!old.equals(_trainScheduleId)) { 237 setDirtyAndFirePropertyChange("AutomationItemTrainScheduleChange", old, _trainScheduleId); // NOI18N 238 } 239 } 240 241 public TrainSchedule getTrainSchedule() { 242 if (getAction() != null && getAction().isOtherMenuEnabled()) { 243 return InstanceManager.getDefault(TrainScheduleManager.class).getScheduleById(_trainScheduleId); 244 } 245 return null; 246 } 247 248 public String getTrainScheduleId() { 249 return _trainScheduleId; 250 } 251 252 public void setMessage(String message) { 253 String old = _message; 254 _message = message; 255 if (!old.equals(message)) { 256 setDirtyAndFirePropertyChange("AutomationItemMessageChange", old, message); // NOI18N 257 } 258 } 259 260 public String getMessage() { 261 return _message; 262 } 263 264 public void setMessageFail(String message) { 265 String old = _messageFail; 266 _messageFail = message; 267 if (!old.equals(message)) { 268 setDirtyAndFirePropertyChange("AutomationItemMessageFailChange", old, message); // NOI18N 269 } 270 } 271 272 public String getMessageFail() { 273 return _messageFail; 274 } 275 276 public boolean isHaltFailureEnabled() { 277 return _haltFail; 278 } 279 280 public void setHaltFailureEnabled(boolean enable) { 281 boolean old = _haltFail; 282 _haltFail = enable; 283 if (old != enable) { 284 setDirtyAndFirePropertyChange("AutomationItemHaltFailureChange", old, enable); // NOI18N 285 } 286 } 287 288 public void setActionRunning(boolean actionRunning) { 289 boolean old = _actionRunning; 290 _actionRunning = actionRunning; 291 if (old != actionRunning) { 292 if (!actionRunning) { 293 setActionRan(true); 294 } 295 firePropertyChange("actionRunningChange", old, actionRunning); // NOI18N 296 } 297 } 298 299 public boolean isActionRunning() { 300 return _actionRunning; 301 } 302 303 public void setActionSuccessful(boolean successful) { 304 boolean old = _actionSuccessful; 305 _actionSuccessful = successful; 306 if (old != successful) { 307 setDirtyAndFirePropertyChange("actionSuccessful", old, successful); // NOI18N 308 } 309 } 310 311 public void setActionRan(boolean ran) { 312 _actionRan = ran; 313 firePropertyChange("actionRan", !ran, ran); // NOI18N 314 } 315 316 public boolean isActionRan() { 317 return _actionRan; 318 } 319 320 public boolean isActionSuccessful() { 321 return _actionSuccessful; 322 } 323 324 public String getStatus() { 325 if (isActionRunning()) 326 return Bundle.getMessage("Running"); 327 if (!isActionRan()) 328 return NONE; 329 if (getAction() != null) 330 return isActionSuccessful() ? getAction().getActionSuccessfulString() : getAction().getActionFailedString(); 331 else 332 return "unknown"; // NOI18N 333 } 334 335 public void reset() { 336 setActionRan(false); 337 setActionSuccessful(false); 338 setGotoBranched(false); 339 } 340 341 /** 342 * Copies item. 343 * @param item The item to copy. 344 */ 345 public void copyItem(AutomationItem item) { 346 setAction(getActionByCode(item.getActionCode())); // must create a new action for each item 347 setAutomationToRun(item.getAutomationToRun()); 348 setGotoAutomationItem(item.getGotoAutomationItem()); //needs an adjustment to work properly 349 setTrain(item.getTrain()); // must set train before route location 350 setRouteLocation(item.getRouteLocation()); 351 setSequenceId(item.getSequenceId()); 352 setTrainSchedule(item.getTrainSchedule()); 353 setMessage(item.getMessage()); 354 setMessageFail(item.getMessageFail()); 355 setHaltFailureEnabled(item.isHaltFailureEnabled()); 356 } 357 358 public static Action getActionByCode(int code) { 359 for (Action action : getActionList()) { 360 if (action.getCode() == code) 361 return action; 362 } 363 return new NoAction(); // default if code not found 364 } 365 366 /** 367 * Gets a list of all known automation actions 368 * 369 * @return list of automation actions 370 */ 371 public static List<Action> getActionList() { 372 List<Action> list = new ArrayList<>(); 373 list.add(new NoAction()); 374 list.add(new BuildTrainAction()); 375 list.add(new BuildTrainIfSelectedAction()); 376 list.add(new PrintTrainManifestAction()); 377 list.add(new PrintTrainManifestIfSelectedAction()); 378 list.add(new PrintTrainBuildReportAction()); 379 list.add(new RunTrainAction()); 380 list.add(new MoveTrainAction()); 381 list.add(new TerminateTrainAction()); 382 list.add(new ResetTrainAction()); 383 list.add(new IsTrainEnRouteAction()); 384 list.add(new WaitTrainAction()); 385 list.add(new WaitTrainTerminatedAction()); 386 list.add(new ActivateTrainScheduleAction()); 387 list.add(new ApplyTrainScheduleAction()); 388 list.add(new SelectTrainAction()); 389 list.add(new DeselectTrainAction()); 390 list.add(new PrintSwitchListAction()); 391 list.add(new UpdateSwitchListAction()); 392 list.add(new WaitSwitchListAction()); 393 list.add(new GenerateSwitchListAction()); 394 list.add(new GenerateSwitchListChangesAction()); 395 list.add(new ResetSwitchListsAction()); 396 list.add(new RunSwitchListAction()); 397 list.add(new RunSwitchListChangesAction()); 398 list.add(new RunAutomationAction()); 399 list.add(new ResumeAutomationAction()); 400 list.add(new StopAutomationAction()); 401 list.add(new MessageYesNoAction()); 402 list.add(new GotoAction()); 403 list.add(new GotoSuccessAction()); 404 list.add(new GotoFailureAction()); 405 list.add(new HaltAction()); 406 return list; 407 } 408 409 public static JComboBox<Action> getActionComboBox() { 410 JComboBox<Action> box = new JComboBox<>(); 411 for (Action action : getActionList()) 412 box.addItem(action); 413 return box; 414 } 415 416 public void dispose() { 417 setDirtyAndFirePropertyChange(DISPOSE, null, DISPOSE); 418 } 419 420 /** 421 * Construct this Entry from XML. This member has to remain synchronized 422 * with the detailed DTD in operations-trains.xml 423 * 424 * @param e Consist XML element 425 */ 426 public AutomationItem(Element e) { 427 org.jdom2.Attribute a; 428 if ((a = e.getAttribute(Xml.ID)) != null) { 429 _id = a.getValue(); 430 } else { 431 log.warn("no id attribute in Automation Item element when reading operations"); 432 } 433 if ((a = e.getAttribute(Xml.SEQUENCE_ID)) != null) { 434 _sequenceId = Integer.parseInt(a.getValue()); 435 } 436 if ((a = e.getAttribute(Xml.ACTION_CODE)) != null) { 437 setAction(getActionByCode(Integer.decode(a.getValue()))); 438 } 439 if ((a = e.getAttribute(Xml.HALT_FAIL)) != null) { 440 _haltFail = a.getValue().equals(Xml.TRUE); 441 } 442 if ((a = e.getAttribute(Xml.ACTION_RAN)) != null) { 443 _actionRan = a.getValue().equals(Xml.TRUE); 444 } 445 if ((a = e.getAttribute(Xml.ACTION_SUCCESSFUL)) != null) { 446 _actionSuccessful = a.getValue().equals(Xml.TRUE); 447 } 448 if ((a = e.getAttribute(Xml.TRAIN_ID)) != null) { 449 _train = InstanceManager.getDefault(TrainManager.class).getTrainById(a.getValue()); 450 } 451 if ((a = e.getAttribute(Xml.ROUTE_LOCATION_ID)) != null && getTrain() != null) { 452 _routeLocation = getTrain().getRoute().getLocationById(a.getValue()); 453 } 454 if ((a = e.getAttribute(Xml.AUTOMATION_ID)) != null) { 455 // in the process of loading automations, so we can't get them now, save id and get later. 456 _automationIdToRun = a.getValue(); 457 } 458 if ((a = e.getAttribute(Xml.GOTO_AUTOMATION_ID)) != null) { 459 // in the process of loading automations, so we can't get them now, save id and get later. 460 _gotoAutomationItemId = a.getValue(); 461 } 462 if ((a = e.getAttribute(Xml.GOTO_AUTOMATION_BRANCHED)) != null) { 463 _gotoAutomationBranched = a.getValue().equals(Xml.TRUE); 464 } 465 if ((a = e.getAttribute(Xml.TRAIN_SCHEDULE_ID)) != null) { 466 _trainScheduleId = a.getValue(); 467 } 468 Element eMessages = e.getChild(Xml.MESSAGES); 469 if (eMessages != null) { 470 Element eMessageOk = eMessages.getChild(Xml.MESSAGE_OK); 471 if (eMessageOk != null && (a = eMessageOk.getAttribute(Xml.MESSAGE)) != null) { 472 _message = a.getValue(); 473 } 474 Element eMessageFail = eMessages.getChild(Xml.MESSAGE_FAIL); 475 if (eMessageFail != null && (a = eMessageFail.getAttribute(Xml.MESSAGE)) != null) { 476 _messageFail = a.getValue(); 477 } 478 } 479 } 480 481 /** 482 * Create an XML element to represent this Entry. This member has to remain 483 * synchronized with the detailed DTD in operations-trains.dtd. 484 * 485 * @return Contents in a JDOM Element 486 */ 487 public Element store() { 488 Element e = new Element(Xml.ITEM); 489 e.setAttribute(Xml.ID, getId()); 490 e.setAttribute(Xml.SEQUENCE_ID, Integer.toString(getSequenceId())); 491 e.setAttribute(Xml.NAME, getActionName()); 492 e.setAttribute(Xml.ACTION_CODE, "0x" + Integer.toHexString(getActionCode())); // NOI18N 493 e.setAttribute(Xml.HALT_FAIL, isHaltFailureEnabled() ? Xml.TRUE : Xml.FALSE); 494 e.setAttribute(Xml.ACTION_RAN, isActionRan() ? Xml.TRUE : Xml.FALSE); 495 e.setAttribute(Xml.ACTION_SUCCESSFUL, isActionSuccessful() ? Xml.TRUE : Xml.FALSE); 496 if (getTrain() != null) { 497 e.setAttribute(Xml.TRAIN_ID, getTrain().getId()); 498 if (getRouteLocation() != null) { 499 e.setAttribute(Xml.ROUTE_LOCATION_ID, getRouteLocation().getId()); 500 } 501 } 502 if (getAutomationToRun() != null) { 503 e.setAttribute(Xml.AUTOMATION_ID, getAutomationToRun().getId()); 504 } 505 if (getGotoAutomationItem() != null) { 506 e.setAttribute(Xml.GOTO_AUTOMATION_ID, getGotoAutomationItem().getId()); 507 e.setAttribute(Xml.GOTO_AUTOMATION_BRANCHED, isGotoBranched() ? Xml.TRUE : Xml.FALSE); 508 } 509 if (getTrainSchedule() != null) { 510 e.setAttribute(Xml.TRAIN_SCHEDULE_ID, getTrainSchedule().getId()); 511 } 512 if (!getMessage().equals(NONE) || !getMessageFail().equals(NONE)) { 513 Element eMessages = new Element(Xml.MESSAGES); 514 e.addContent(eMessages); 515 Element eMessageOk = new Element(Xml.MESSAGE_OK); 516 eMessageOk.setAttribute(Xml.MESSAGE, getMessage()); 517 Element eMessageFail = new Element(Xml.MESSAGE_FAIL); 518 eMessageFail.setAttribute(Xml.MESSAGE, getMessageFail()); 519 eMessages.addContent(eMessageOk); 520 eMessages.addContent(eMessageFail); 521 } 522 return e; 523 } 524 525 @Override 526 public void propertyChange(java.beans.PropertyChangeEvent e) { 527 if (Control.SHOW_PROPERTY) { 528 log.debug("AutomationItem id ({}) sees property change: ({}) old: ({}) new: ({})", 529 getId(), e.getPropertyName(), e.getOldValue(), e.getNewValue()); // NOI18N 530 } 531 } 532 533 protected void setDirtyAndFirePropertyChange(String p, Object old, Object n) { 534 // set dirty 535 InstanceManager.getDefault(TrainManagerXml.class).setDirty(true); 536 firePropertyChange(p, old, n); 537 } 538 539 private final static Logger log = LoggerFactory.getLogger(AutomationItem.class); 540 541}