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