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