001package jmri.implementation;
002
003import java.awt.event.ActionListener;
004import javax.swing.Timer;
005import jmri.Audio;
006import jmri.Conditional;
007import jmri.ConditionalAction;
008import jmri.InstanceManager;
009import jmri.Light;
010import jmri.Memory;
011import jmri.NamedBean;
012import jmri.NamedBeanHandle;
013import jmri.Route;
014import jmri.RouteManager;
015import jmri.Sensor;
016import jmri.SignalHead;
017import jmri.Turnout;
018import jmri.jmrit.Sound;
019import jmri.jmrit.beantable.LogixTableAction;
020import jmri.jmrit.logix.OBlockManager;
021import jmri.jmrit.logix.Warrant;
022import jmri.jmrit.logix.WarrantManager;
023import org.slf4j.Logger;
024import org.slf4j.LoggerFactory;
025
026/**
027 * The consequent of the antecedent of the conditional proposition. The data for
028 * the action to be taken when a Conditional calculates to True
029 * <p>
030 * This is in the implementations package because of a Swing dependence via the
031 * times. Java 1.5 or Java 1.6 might make it possible to break that, which will
032 * simplify things.
033 *
034 * @author Pete Cressman Copyright (C) 2009, 2010, 2011
035 * @author Matthew Harris copyright (c) 2009
036 */
037public class DefaultConditionalAction implements ConditionalAction {
038
039    private int _option = Conditional.ACTION_OPTION_ON_CHANGE_TO_TRUE;
040    private Conditional.Action _type = Conditional.Action.NONE;
041    private String _deviceName = " ";
042    private int _actionData = 0;
043    private String _actionString = "";
044    private NamedBeanHandle<?> _namedBean = null;
045
046    private Timer _timer = null;
047    private ActionListener _listener = null;
048    private boolean _timerActive = false;
049    private boolean _indirectAction = false;
050    private Sound _sound = null;
051
052    static final java.util.ResourceBundle rbx = java.util.ResourceBundle.getBundle("jmri.jmrit.conditional.ConditionalBundle");
053    protected jmri.NamedBeanHandleManager nbhm = jmri.InstanceManager.getDefault(jmri.NamedBeanHandleManager.class);
054
055    public DefaultConditionalAction() {
056    }
057
058    public DefaultConditionalAction(int option, Conditional.Action type, String name, int actionData, String actionStr) {
059        _option = option;
060        _type = type;
061        _deviceName = name;
062        _actionData = actionData;
063        _actionString = actionStr;
064
065        NamedBean bean = getIndirectBean(_deviceName);
066        if (bean == null) {
067            bean = getActionBean(_deviceName);
068        }
069        if (bean != null) {
070            _namedBean = nbhm.getNamedBeanHandle(_deviceName, bean);
071        } else {
072            _namedBean = null;
073        }
074    }
075
076    @Override
077    public boolean equals(Object obj) {
078        if (obj == this) {
079            return true;
080        }
081        if (obj == null) {
082            return false;
083        }
084
085        if (!(getClass() == obj.getClass())) {
086            return false;
087        } else {
088            DefaultConditionalAction p = (DefaultConditionalAction) obj;
089            if ((p._option != this._option)
090                    || (p._type != this._type)
091                    || (p._actionData != this._actionData)) {
092                return false;
093            }
094
095            if ((p._namedBean == null && this._namedBean != null)
096                    || (p._namedBean != null && this._namedBean == null)
097                    || (p._namedBean != null && this._namedBean != null && !p._namedBean.equals(this._namedBean))) {
098                return false;
099            }
100
101            if ((p._deviceName == null && this._deviceName != null)
102                    || (p._deviceName != null && this._deviceName == null)
103                    || (p._deviceName != null && this._deviceName != null && !p._deviceName.equals(this._deviceName))) {
104                return false;
105            }
106
107            if ((p._actionString == null && this._actionString != null)
108                    || (p._actionString != null && this._actionString == null)
109                    || (p._actionString != null && this._actionString != null && !p._actionString.equals(this._actionString))) {
110                return false;
111            }
112
113        }
114        return true;
115    }
116
117    @Override
118    public int hashCode() {
119        int hash = _option * 1000 + _type.getIntValue() * 1000 * 1000 + _actionData;
120        if (_deviceName != null) {
121            hash += _deviceName.hashCode();
122        }
123        return hash;
124    }
125
126    /**
127     * If this is an indirect reference, return the Memory bean.
128     *
129     */
130    private Memory getIndirectBean(String devName) {
131        if (devName != null && devName.length() > 0 && devName.charAt(0) == '@') {
132            String memName = devName.substring(1);
133            Memory m = InstanceManager.memoryManagerInstance().getMemory(memName);
134            if (m != null) {
135                _indirectAction = true;
136                return m;
137            }
138            log.error("\"{}\" invalid indirect memory name in action {} of type {}", devName, _actionString, _type);
139        } else {
140            _indirectAction = false;
141        }
142        return null;
143    }
144
145    /**
146     * Return the device bean that will do the action.
147     *
148     */
149    private NamedBean getActionBean(String devName) {
150        if (devName == null) return null;
151        NamedBean bean = null;
152        try {
153            switch (_type.getItemType()) {
154                case SENSOR:
155                    try {
156                        bean = InstanceManager.sensorManagerInstance().provideSensor(devName);
157                    } catch (IllegalArgumentException e) {
158                        bean = null;
159                        log.error("invalid sensor name= \"{}\" in conditional action", devName);
160                    }
161                    break;
162                case TURNOUT:
163                    try {
164                        bean = InstanceManager.turnoutManagerInstance().provideTurnout(devName);
165                    } catch (IllegalArgumentException e) {
166                        bean = null;
167                        log.error("invalid turnout name= \"{}\" in conditional action", devName);
168                    }
169                    break;
170                case MEMORY:
171                    try {
172                        bean = InstanceManager.memoryManagerInstance().provideMemory(devName);
173                    } catch (IllegalArgumentException e) {
174                        bean = null;
175                        log.error("invalid memory name= \"{}\" in conditional action", devName);
176                    }
177                    break;
178                case LIGHT:
179                    try {
180                        bean = InstanceManager.lightManagerInstance().getLight(devName);
181                    } catch (IllegalArgumentException e) {
182                        bean = null;
183                        log.error("invalid light name= \"{}\" in conditional action", devName);
184                    }
185                    break;
186                case SIGNALMAST:
187                    try {
188                        bean = InstanceManager.getDefault(jmri.SignalMastManager.class).provideSignalMast(devName);
189                    } catch (IllegalArgumentException e) {
190                        bean = null;
191                        log.error("invalid signal mast name= \"{}\" in conditional action", devName);
192                    }
193                    break;
194                case SIGNALHEAD:
195                    try {
196                        bean = InstanceManager.getDefault(jmri.SignalHeadManager.class).getSignalHead(devName);
197                    } catch (IllegalArgumentException e) {
198                        bean = null;
199                        log.error("invalid signal head name= \"{}\" in conditional action", devName);
200                    }
201                    break;
202                case WARRANT:
203                    try {
204                        bean = InstanceManager.getDefault(WarrantManager.class).getWarrant(devName);
205                    } catch (IllegalArgumentException e) {
206                        bean = null;
207                        log.error("invalid Warrant name= \"{}\" in conditional action", devName);
208                    }
209                    break;
210                case OBLOCK:
211                    try {
212                        bean = InstanceManager.getDefault(OBlockManager.class).getOBlock(devName);
213                    } catch (IllegalArgumentException e) {
214                        bean = null;
215                        log.error("invalid OBlock name= \"{}\" in conditional action", devName);
216                    }
217                    break;
218                case ENTRYEXIT:
219                    try {
220                        bean = jmri.InstanceManager.getDefault(jmri.jmrit.entryexit.EntryExitPairs.class).getNamedBean(devName);
221                    } catch (IllegalArgumentException e) {
222                        bean = null;
223                        log.error("invalid NX name= \"{}\" in conditional action", devName);
224                    }
225                    break;
226                case LOGIX:
227                    try {
228                        bean = jmri.InstanceManager.getDefault(jmri.LogixManager.class).getLogix(devName);
229                    } catch (IllegalArgumentException e) {
230                        bean = null;
231                        log.error("invalid Logix name= \"{}\" in conditional action", devName);
232                    }
233                    break;
234                default:
235                    if (getType() == Conditional.Action.TRIGGER_ROUTE) {
236                        try {
237                            bean = InstanceManager.getDefault(RouteManager.class).getRoute(devName);
238                        } catch (IllegalArgumentException e) {
239                            bean = null;
240                            log.error("invalid Route name= \"{}\" in conditional action", devName);
241                        }
242                    }
243            }
244        } catch (java.lang.NumberFormatException ex) {
245            // ingonred, can be considered normal if the logixs are loaded prior to any other beans
246        }
247        return bean;
248    }
249
250    /**
251     * The consequent device or element type.
252     */
253    @Override
254    public Conditional.Action getType() {
255        return _type;
256    }
257
258    @Override
259    public void setType(Conditional.Action type) {
260        _type = type;
261    }
262
263    /**
264     * Set type from user name for it.
265     */
266    @Override
267    public void setType(String type) {
268        _type = stringToActionType(type);
269    }
270
271    /**
272     * Name of the device or element that is affected.
273     */
274    @Override
275    public String getDeviceName() {
276        if (_namedBean != null) {
277            return _namedBean.getName();
278        }
279        /* As we have a trigger for something using the action, then hopefully
280         all the managers have been loaded and we can get the bean, which prevented
281         the bean from being loaded in the first place */
282        setDeviceName(_deviceName);
283        return _deviceName;
284    }
285
286    @Override
287    public void setDeviceName(String deviceName) {
288        _deviceName = deviceName;
289        NamedBean bean = getIndirectBean(_deviceName);
290        if (bean == null) {
291            bean = getActionBean(_deviceName);
292        }
293        if (bean != null) {
294            _namedBean = nbhm.getNamedBeanHandle(_deviceName, bean);
295        } else {
296            _namedBean = null;
297        }
298    }
299
300    @Override
301    public NamedBeanHandle<?> getNamedBean() {
302        if (_indirectAction) {
303            Memory m = (Memory) (_namedBean.getBean());
304            String actionName = (String) m.getValue();
305            NamedBean bean = getActionBean(actionName);
306            if (bean != null) {
307                return nbhm.getNamedBeanHandle(actionName, bean);
308            } else {
309                return null;
310            }
311        }
312        return _namedBean;
313    }
314
315    @Override
316    public NamedBean getBean() {
317        if (_namedBean != null) {
318            NamedBeanHandle<?> handle = getNamedBean();
319            if (handle == null) return null;
320            return handle.getBean();
321        }
322        setDeviceName(_deviceName); //ReApply name as that will create namedBean, save replicating it here
323        if (_namedBean != null) {
324            return getNamedBean().getBean();
325        }
326        return null;
327    }
328
329    /**
330     * Options on when action is taken.
331     */
332    @Override
333    public int getOption() {
334        return _option;
335    }
336
337    @Override
338    public void setOption(int option) {
339        _option = option;
340    }
341
342    /**
343     * Integer data for action.
344     */
345    @Override
346    public int getActionData() {
347        return _actionData;
348    }
349
350    @Override
351    public void setActionData(int actionData) {
352        _actionData = actionData;
353    }
354
355    /**
356     * Set action data from user name for it.
357     */
358    @Override
359    public void setActionData(String actionData) {
360        _actionData = stringToActionData(actionData);
361    }
362
363    /**
364     * String data for action.
365     */
366    @Override
367    public String getActionString() {
368        if (_actionString == null) {
369            _actionString = getTypeString();
370        }
371        return _actionString;
372    }
373
374    @Override
375    public void setActionString(String actionString) {
376        _actionString = actionString;
377    }
378
379    /*
380     * Get timer for delays and other timed events.
381     */
382    @Override
383    public Timer getTimer() {
384        return _timer;
385    }
386
387    /*
388     * Set timer for delays and other timed events.
389     */
390    @Override
391    public void setTimer(Timer timer) {
392        _timer = timer;
393    }
394
395    @Override
396    public boolean isTimerActive() {
397        return _timerActive;
398    }
399
400    @Override
401    public void startTimer() {
402        if (_timer != null) {
403            _timer.start();
404            _timerActive = true;
405        } else {
406            log.error("timer is null for {} of type {}", _deviceName, getTypeString());
407        }
408    }
409
410    @Override
411    public void stopTimer() {
412        if (_timer != null) {
413            _timer.stop();
414            _timerActive = false;
415        }
416    }
417
418    /*
419     * Set listener for delays and other timed events.
420     */
421    @Override
422    public ActionListener getListener() {
423        return _listener;
424    }
425
426    /*
427     * set listener for delays and other timed events
428     */
429    @Override
430    public void setListener(ActionListener listener) {
431        _listener = listener;
432    }
433
434    /**
435     * Get Sound file.
436     */
437    @Override
438    public Sound getSound() {
439        return _sound;
440    }
441
442    /**
443     * Set the sound file.
444     *
445     * @param sound the new sound file
446     */
447    protected void setSound(Sound sound) {
448        _sound = sound;
449    }
450
451    /*
452     * Methods that return user interface strings ****
453     */
454
455    /**
456     * @return name of this consequent type
457     */
458    @Override
459    public String getTypeString() {
460        return _type.toString();
461    }
462
463    /**
464     * @return name of the option for this consequent type
465     */
466    @Override
467    public String getOptionString(boolean type) {
468        return getOptionString(_option, type);
469    }
470
471    @Override
472    public String getActionDataString() {
473        return getActionDataString(_type, _actionData);
474    }
475
476    /**
477     * Convert Variable Type to Text String.
478     *
479     * @.param t the Action type
480     * @.return a human readable description of the type or an empty String
481     *./
482    public static String getItemTypeString(int t) {
483        switch (t) {
484            case Conditional.ITEM_TYPE_SENSOR:
485                return (Bundle.getMessage("BeanNameSensor"));
486            case Conditional.ITEM_TYPE_TURNOUT:
487                return (Bundle.getMessage("BeanNameTurnout"));
488            case Conditional.ITEM_TYPE_LIGHT:
489                return (Bundle.getMessage("BeanNameLight"));
490            case Conditional.ITEM_TYPE_SIGNALHEAD:
491                return (Bundle.getMessage("BeanNameSignalHead"));
492            case Conditional.ITEM_TYPE_SIGNALMAST:
493                return (Bundle.getMessage("BeanNameSignalMast"));
494            case Conditional.ITEM_TYPE_MEMORY:
495                return (Bundle.getMessage("BeanNameMemory"));
496            case Conditional.ITEM_TYPE_LOGIX:
497                return (Bundle.getMessage("BeanNameLogix"));
498            case Conditional.ITEM_TYPE_WARRANT:
499                return (Bundle.getMessage("BeanNameWarrant"));
500            case Conditional.ITEM_TYPE_OBLOCK:
501                return (Bundle.getMessage("BeanNameOBlock"));
502            case Conditional.ITEM_TYPE_ENTRYEXIT:
503                return (Bundle.getMessage("BeanNameEntryExit"));
504            case Conditional.ITEM_TYPE_CLOCK:
505                return (Bundle.getMessage("FastClock"));
506            case Conditional.ITEM_TYPE_AUDIO:
507                return (Bundle.getMessage("BeanNameAudio"));
508            case Conditional.ITEM_TYPE_SCRIPT:
509                return (Bundle.getMessage("Script"));
510            case Conditional.ITEM_TYPE_OTHER:
511                return (rbx.getString("Other"));
512            default:
513                // fall through
514                break;
515        }
516        return "";
517    }
518*/
519    /**
520     * Convert Consequent Type to text String.
521     *
522     * @.param t the Action type
523     * @.return a human readable description of the type or an empty String
524     *./
525    public static String getActionTypeString(int t) {
526        switch (t) {
527            case Conditional.ACTION_NONE:
528                return (rbx.getString("ActionNone"));
529            case Conditional.ACTION_SET_TURNOUT:
530                return (rbx.getString("ActionSetTurnout"));
531            case Conditional.ACTION_SET_SIGNAL_APPEARANCE:
532                return (rbx.getString("ActionSetSignal"));
533            case Conditional.ACTION_SET_SIGNAL_HELD:
534                return (rbx.getString("ActionSetSignalHeld"));
535            case Conditional.ACTION_CLEAR_SIGNAL_HELD:
536                return (rbx.getString("ActionClearSignalHeld"));
537            case Conditional.ACTION_SET_SIGNAL_DARK:
538                return (rbx.getString("ActionSetSignalDark"));
539            case Conditional.ACTION_SET_SIGNAL_LIT:
540                return (rbx.getString("ActionSetSignalLit"));
541            case Conditional.ACTION_TRIGGER_ROUTE:
542                return (rbx.getString("ActionTriggerRoute"));
543            case Conditional.ACTION_SET_SENSOR:
544                return (rbx.getString("ActionSetSensor"));
545            case Conditional.ACTION_DELAYED_SENSOR:
546                return (rbx.getString("ActionDelayedSensor"));
547            case Conditional.ACTION_SET_LIGHT:
548                return (rbx.getString("ActionSetLight"));
549            case Conditional.ACTION_SET_MEMORY:
550                return (rbx.getString("ActionSetMemory"));
551            case Conditional.ACTION_ENABLE_LOGIX:
552                return (rbx.getString("ActionEnableLogix"));
553            case Conditional.ACTION_DISABLE_LOGIX:
554                return (rbx.getString("ActionDisableLogix"));
555            case Conditional.ACTION_PLAY_SOUND:
556                return (rbx.getString("ActionPlaySound"));
557            case Conditional.ACTION_RUN_SCRIPT:
558                return (rbx.getString("ActionRunScript"));
559            case Conditional.ACTION_DELAYED_TURNOUT:
560                return (rbx.getString("ActionDelayedTurnout"));
561            case Conditional.ACTION_LOCK_TURNOUT:
562                return (rbx.getString("ActionTurnoutLock"));
563            case Conditional.ACTION_RESET_DELAYED_SENSOR:
564                return (rbx.getString("ActionResetDelayedSensor"));
565            case Conditional.ACTION_CANCEL_SENSOR_TIMERS:
566                return (rbx.getString("ActionCancelSensorTimers"));
567            case Conditional.ACTION_RESET_DELAYED_TURNOUT:
568                return (rbx.getString("ActionResetDelayedTurnout"));
569            case Conditional.ACTION_CANCEL_TURNOUT_TIMERS:
570                return (rbx.getString("ActionCancelTurnoutTimers"));
571            case Conditional.ACTION_SET_FAST_CLOCK_TIME:
572                return (rbx.getString("ActionSetFastClockTime"));
573            case Conditional.ACTION_START_FAST_CLOCK:
574                return (rbx.getString("ActionStartFastClock"));
575            case Conditional.ACTION_STOP_FAST_CLOCK:
576                return (rbx.getString("ActionStopFastClock"));
577            case Conditional.ACTION_COPY_MEMORY:
578                return (rbx.getString("ActionCopyMemory"));
579            case Conditional.ACTION_SET_LIGHT_INTENSITY:
580                return (rbx.getString("ActionSetLightIntensity"));
581            case Conditional.ACTION_SET_LIGHT_TRANSITION_TIME:
582                return (rbx.getString("ActionSetLightTransitionTime"));
583            case Conditional.ACTION_CONTROL_AUDIO:
584                return (rbx.getString("ActionControlAudio"));
585            case Conditional.ACTION_JYTHON_COMMAND:
586                return (rbx.getString("ActionJythonCommand"));
587            case Conditional.ACTION_ALLOCATE_WARRANT_ROUTE:
588                return (rbx.getString("ActionAllocateWarrant"));
589            case Conditional.ACTION_DEALLOCATE_WARRANT_ROUTE:
590                return (rbx.getString("ActionDeallocateWarrant"));
591            case Conditional.ACTION_SET_ROUTE_TURNOUTS:
592                return (rbx.getString("ActionSetWarrantTurnouts"));
593            case Conditional.ACTION_AUTO_RUN_WARRANT:
594                return (rbx.getString("ActionAutoRunWarrant"));
595            case Conditional.ACTION_MANUAL_RUN_WARRANT:
596                return (rbx.getString("ActionManualRunWarrant"));
597            case Conditional.ACTION_CONTROL_TRAIN:
598                return (rbx.getString("ActionControlTrain"));
599            case Conditional.ACTION_SET_TRAIN_ID:
600                return (rbx.getString("ActionSetTrainId"));
601            case Conditional.ACTION_SET_TRAIN_NAME:
602                return (rbx.getString("ActionSetTrainName"));
603            case Conditional.ACTION_SET_SIGNALMAST_ASPECT:
604                return (rbx.getString("ActionSetSignalMastAspect"));
605            case Conditional.ACTION_THROTTLE_FACTOR:
606                return (rbx.getString("ActionSetThrottleFactor"));
607            case Conditional.ACTION_SET_SIGNALMAST_HELD:
608                return (rbx.getString("ActionSetSignalMastHeld"));
609            case Conditional.ACTION_CLEAR_SIGNALMAST_HELD:
610                return (rbx.getString("ActionClearSignalMastHeld"));
611            case Conditional.ACTION_SET_SIGNALMAST_DARK:
612                return (rbx.getString("ActionSetSignalMastDark"));
613            case Conditional.ACTION_SET_SIGNALMAST_LIT:
614                return (rbx.getString("ActionClearSignalMastDark"));
615            case Conditional.ACTION_SET_BLOCK_VALUE:
616                return (rbx.getString("ActionSetBlockValue"));
617            case Conditional.ACTION_SET_BLOCK_ERROR:
618                return (rbx.getString("ActionSetBlockError"));
619            case Conditional.ACTION_CLEAR_BLOCK_ERROR:
620                return (rbx.getString("ActionClearBlockError"));
621            case Conditional.ACTION_DEALLOCATE_BLOCK:
622                return (rbx.getString("ActionDeallocateBlock"));
623            case Conditional.ACTION_SET_BLOCK_OUT_OF_SERVICE:
624                return (rbx.getString("ActionSetBlockOutOfService"));
625            case Conditional.ACTION_SET_BLOCK_IN_SERVICE:
626                return (rbx.getString("ActionBlockInService"));
627            case Conditional.ACTION_SET_NXPAIR_ENABLED:
628                return (rbx.getString("ActionNXPairEnabled"));
629            case Conditional.ACTION_SET_NXPAIR_DISABLED:
630                return (rbx.getString("ActionNXPairDisabled"));
631            case Conditional.ACTION_SET_NXPAIR_SEGMENT:
632                return (rbx.getString("ActionNXPairSegment"));
633            default:
634                // fall through
635                break;
636        }
637        log.warn("Unexpected parameter to getActionTypeString({})", t);
638        return ("");
639    }
640*/
641    /**
642     * Convert consequent option to String.
643     *
644     * @param opt  the option
645     * @param type true if option is a change; false if option is a trigger
646     * @return a human readable description of the option or an empty String
647     */
648    public static String getOptionString(int opt, boolean type) {
649        switch (opt) {
650            case Conditional.ACTION_OPTION_ON_CHANGE_TO_TRUE:
651                if (type) {
652                    return (rbx.getString("OnChangeToTrue"));
653                } else {
654                    return (rbx.getString("OnTriggerToTrue"));
655                }
656            case Conditional.ACTION_OPTION_ON_CHANGE_TO_FALSE:
657                if (type) {
658                    return (rbx.getString("OnChangeToFalse"));
659                } else {
660                    return (rbx.getString("OnTriggerToFalse"));
661                }
662            case Conditional.ACTION_OPTION_ON_CHANGE:
663                if (type) {
664                    return (rbx.getString("OnChange"));
665                } else {
666                    return (rbx.getString("OnTrigger"));
667                }
668            default:
669                // fall through
670                break;
671        }
672        log.warn("Unexpected parameter to getOptionString({})", opt);
673        return "";
674    }
675
676    /**
677     * Get action type from a String.
678     *
679     * @param str the string to get the type for
680     * @return the type or 0 if str is not a recognized action
681     */
682    public static Conditional.Action stringToActionType(String str) {
683        if (str != null) {
684            for (Conditional.Action action : Conditional.Action.values()) {
685                if (str.equals(action.toString())) {
686                    return action;
687                }
688            }
689        }
690        log.warn("Unexpected parameter to stringToActionType({})", str);
691        return Conditional.Action.NONE;
692    }
693
694    /**
695     * Get action Data from a String.
696     *
697     * @param str the string to get the action data for
698     * @return the action data of -1 is str is not recognized
699     */
700    public static int stringToActionData(String str) {
701        if (str.equals(Bundle.getMessage("TurnoutStateClosed"))) {
702            return Turnout.CLOSED;
703        } else if (str.equals(Bundle.getMessage("TurnoutStateThrown"))) {
704            return Turnout.THROWN;
705        } else if (str.equals(Bundle.getMessage("SensorStateActive"))) {
706            return Sensor.ACTIVE;
707        } else if (str.equals(Bundle.getMessage("SensorStateInactive"))) {
708            return Sensor.INACTIVE;
709        } else if (str.equals(rbx.getString("LightOn"))) {
710            return Light.ON;
711        } else if (str.equals(rbx.getString("LightOff"))) {
712            return Light.OFF;
713        } else if (str.equals(rbx.getString("TurnoutUnlock"))) {
714            return Turnout.UNLOCKED;
715        } else if (str.equals(rbx.getString("TurnoutLock"))) {
716            return Turnout.LOCKED;
717        } else if (str.equals(Bundle.getMessage("SignalHeadStateRed"))) {
718            return SignalHead.RED;
719        } else if (str.equals(Bundle.getMessage("SignalHeadStateYellow"))) {
720            return SignalHead.YELLOW;
721        } else if (str.equals(Bundle.getMessage("SignalHeadStateGreen"))) {
722            return SignalHead.GREEN;
723        } else if (str.equals(Bundle.getMessage("SignalHeadStateDark"))) {
724            return SignalHead.DARK;
725        } else if (str.equals(Bundle.getMessage("SignalHeadStateFlashingRed"))) {
726            return SignalHead.FLASHRED;
727        } else if (str.equals(Bundle.getMessage("SignalHeadStateFlashingYellow"))) {
728            return SignalHead.FLASHYELLOW;
729        } else if (str.equals(Bundle.getMessage("SignalHeadStateFlashingGreen"))) {
730            return SignalHead.FLASHGREEN;
731        } else if (str.equals(Bundle.getMessage("SignalHeadStateLunar"))) {
732            return SignalHead.LUNAR;
733        } else if (str.equals(Bundle.getMessage("SignalHeadStateFlashingLunar"))) {
734            return SignalHead.FLASHLUNAR;
735        } else if (str.equals(rbx.getString("AudioSourcePlay"))) {
736            return Audio.CMD_PLAY;
737        } else if (str.equals(rbx.getString("AudioSourceStop"))) {
738            return Audio.CMD_STOP;
739        } else if (str.equals(rbx.getString("AudioSourcePlayToggle"))) {
740            return Audio.CMD_PLAY_TOGGLE;
741        } else if (str.equals(rbx.getString("AudioSourcePause"))) {
742            return Audio.CMD_PAUSE;
743        } else if (str.equals(rbx.getString("AudioSourceResume"))) {
744            return Audio.CMD_RESUME;
745        } else if (str.equals(rbx.getString("AudioSourcePauseToggle"))) {
746            return Audio.CMD_PAUSE_TOGGLE;
747        } else if (str.equals(rbx.getString("AudioSourceRewind"))) {
748            return Audio.CMD_REWIND;
749        } else if (str.equals(rbx.getString("AudioSourceFadeIn"))) {
750            return Audio.CMD_FADE_IN;
751        } else if (str.equals(rbx.getString("AudioSourceFadeOut"))) {
752            return Audio.CMD_FADE_OUT;
753        } else if (str.equals(rbx.getString("AudioResetPosition"))) {
754            return Audio.CMD_RESET_POSITION;
755        }
756        // empty strings can occur frequently with types that have no integer data
757        if (str.length() > 0) {
758            log.warn("Unexpected parameter to stringToActionData({})", str);
759        }
760        return -1;
761    }
762
763    public static String getActionDataString(Conditional.Action t, int data) {
764        switch (t) {
765            case SET_TURNOUT:
766            case DELAYED_TURNOUT:
767            case RESET_DELAYED_TURNOUT:
768                if (data == Turnout.CLOSED) {
769                    return (Bundle.getMessage("TurnoutStateClosed"));
770                } else if (data == Turnout.THROWN) {
771                    return (Bundle.getMessage("TurnoutStateThrown"));
772                } else if (data == Route.TOGGLE) {
773                    return (Bundle.getMessage("Toggle"));
774                }
775                break;
776            case SET_SIGNAL_APPEARANCE:
777                return DefaultSignalHead.getDefaultStateName(data);
778            case SET_SENSOR:
779            case DELAYED_SENSOR:
780            case RESET_DELAYED_SENSOR:
781                if (data == Sensor.ACTIVE) {
782                    return (Bundle.getMessage("SensorStateActive"));
783                } else if (data == Sensor.INACTIVE) {
784                    return (Bundle.getMessage("SensorStateInactive"));
785                } else if (data == Route.TOGGLE) {
786                    return (Bundle.getMessage("Toggle"));
787                }
788                break;
789            case SET_LIGHT:
790                if (data == Light.ON) {
791                    return (rbx.getString("LightOn"));
792                } else if (data == Light.OFF) {
793                    return (rbx.getString("LightOff"));
794                } else if (data == Route.TOGGLE) {
795                    return (Bundle.getMessage("Toggle"));
796                }
797                break;
798            case LOCK_TURNOUT:
799                if (data == Turnout.UNLOCKED) {
800                    return (rbx.getString("TurnoutUnlock"));
801                } else if (data == Turnout.LOCKED) {
802                    return (rbx.getString("TurnoutLock"));
803                } else if (data == Route.TOGGLE) {
804                    return (Bundle.getMessage("Toggle"));
805                }
806                break;
807            case CONTROL_AUDIO:
808                switch (data) {
809                    case Audio.CMD_PLAY:
810                        return (rbx.getString("AudioSourcePlay"));
811                    case Audio.CMD_STOP:
812                        return (rbx.getString("AudioSourceStop"));
813                    case Audio.CMD_PLAY_TOGGLE:
814                        return (rbx.getString("AudioSourcePlayToggle"));
815                    case Audio.CMD_PAUSE:
816                        return (rbx.getString("AudioSourcePause"));
817                    case Audio.CMD_RESUME:
818                        return (rbx.getString("AudioSourceResume"));
819                    case Audio.CMD_PAUSE_TOGGLE:
820                        return (rbx.getString("AudioSourcePauseToggle"));
821                    case Audio.CMD_REWIND:
822                        return (rbx.getString("AudioSourceRewind"));
823                    case Audio.CMD_FADE_IN:
824                        return (rbx.getString("AudioSourceFadeIn"));
825                    case Audio.CMD_FADE_OUT:
826                        return (rbx.getString("AudioSourceFadeOut"));
827                    case Audio.CMD_RESET_POSITION:
828                        return (rbx.getString("AudioResetPosition"));
829                    default:
830                        log.error("Unhandled Audio operation command: {}", data);
831                        break;
832                }
833                break;
834            case CONTROL_TRAIN:
835                switch (data) {
836                    case Warrant.HALT:
837                        return (rbx.getString("WarrantHalt"));
838                    case Warrant.RESUME:
839                        return (rbx.getString("WarrantResume"));
840                    case Warrant.RETRY_FWD:
841                        return (rbx.getString("WarrantMoveToNext"));
842                    case Warrant.SPEED_UP:
843                        return (rbx.getString("WarrantSpeedUp"));
844                    case Warrant.STOP:
845                        return (rbx.getString("WarrantStop"));
846                    case Warrant.ESTOP:
847                        return (rbx.getString("WarrantEStop"));
848                    case Warrant.ABORT:
849                        return (rbx.getString("WarrantAbort"));
850                    default:
851                        log.error("Unhandled Warrant control: {}", data);
852                }
853                break;
854            default:
855                // fall through
856                break;
857        }
858        return "";
859    }
860
861    @Override
862    public String description(boolean triggerType) {
863        String str = getOptionString(triggerType) + ", " + getTypeString();
864        if (_deviceName.length() > 0) {
865            switch (_type) {
866                case CANCEL_TURNOUT_TIMERS:
867                case SET_SIGNAL_HELD:
868                case CLEAR_SIGNAL_HELD:
869                case SET_SIGNAL_DARK:
870                case SET_SIGNAL_LIT:
871                case TRIGGER_ROUTE:
872                case CANCEL_SENSOR_TIMERS:
873                case SET_MEMORY:
874                case ENABLE_LOGIX:
875                case DISABLE_LOGIX:
876                case COPY_MEMORY:
877                case SET_LIGHT_INTENSITY:
878                case SET_LIGHT_TRANSITION_TIME:
879                case ALLOCATE_WARRANT_ROUTE:
880                case DEALLOCATE_WARRANT_ROUTE:
881                case SET_SIGNALMAST_HELD:
882                case CLEAR_SIGNALMAST_HELD:
883                case SET_SIGNALMAST_DARK:
884                case SET_SIGNALMAST_LIT:
885                case SET_BLOCK_ERROR:
886                case CLEAR_BLOCK_ERROR:
887                case DEALLOCATE_BLOCK:
888                case SET_BLOCK_OUT_OF_SERVICE:
889                case SET_BLOCK_IN_SERVICE:
890                    str = str + ", \"" + _deviceName + "\".";
891                    break;
892                case SET_NXPAIR_ENABLED:
893                case SET_NXPAIR_DISABLED:
894                case SET_NXPAIR_SEGMENT:
895                    str = str + ", \"" + getBean().getUserName() + "\".";
896                    break;
897                case SET_ROUTE_TURNOUTS:
898                case AUTO_RUN_WARRANT:
899                case MANUAL_RUN_WARRANT:
900                    str = str + " " + rbx.getString("onWarrant") + ", \"" + _deviceName + "\".";
901                    break;
902                case SET_SENSOR:
903                case SET_TURNOUT:
904                case SET_LIGHT:
905                case LOCK_TURNOUT:
906                case RESET_DELAYED_SENSOR:
907                case SET_SIGNAL_APPEARANCE:
908                case RESET_DELAYED_TURNOUT:
909                case DELAYED_TURNOUT:
910                case DELAYED_SENSOR:
911                case CONTROL_AUDIO:
912                    str = str + ", \"" + _deviceName + "\" " + rbx.getString("to")
913                            + " " + getActionDataString();
914                    break;
915                case GET_TRAIN_LOCATION:
916                case GET_BLOCK_WARRANT:
917                case GET_BLOCK_TRAIN_NAME:
918                    str = str + " \"" + _deviceName + "\" " + rbx.getString("intoMemory")
919                              + " " + _actionString;
920                    break;
921                case SET_SIGNALMAST_ASPECT:
922                    str = str + ", \"" + _deviceName + "\" " + rbx.getString("to")
923                            + " " + _actionString;
924                    break;
925                case CONTROL_TRAIN:
926                    str = str + " " + rbx.getString("onWarrant") + " \"" + _deviceName + "\" "
927                            + rbx.getString("to") + " " + getActionDataString();
928                    break;
929                default:
930                    break; // nothing needed for others
931            }
932        }
933        if (_actionString.length() > 0) {
934            switch (_type) {
935                case SET_MEMORY:
936                case COPY_MEMORY:
937                    str = str + " " + rbx.getString("to") + " " + _actionString + ".";
938                    break;
939                case PLAY_SOUND:
940                case RUN_SCRIPT:
941                    str = str + " " + rbx.getString("FromFile") + " " + _actionString + ".";
942                    break;
943                case RESET_DELAYED_TURNOUT:
944                case RESET_DELAYED_SENSOR:
945                case DELAYED_TURNOUT:
946                case DELAYED_SENSOR:
947                    str = str + rbx.getString("After") + " ";
948                    try {
949                        Float.parseFloat(_actionString);
950                        str = str + _actionString + " " + rbx.getString("Seconds") + ".";
951                    } catch (NumberFormatException nfe) {
952                        str = str + _actionString + " " + rbx.getString("ValueInMemory")
953                                + " " + rbx.getString("Seconds") + ".";
954                    }
955                    break;
956                case SET_LIGHT_TRANSITION_TIME:
957                case SET_LIGHT_INTENSITY:
958                    try {
959                        //int t = Integer.parseInt(_actionString);
960                        str = str + " " + rbx.getString("to") + " " + _actionString + ".";
961                    } catch (NumberFormatException nfe) {
962                        str = str + " " + rbx.getString("to") + " " + _actionString + " "
963                                + rbx.getString("ValueInMemory") + ".";
964                    }
965                    break;
966                case JYTHON_COMMAND:
967                    str = str + " " + rbx.getString("ExecJythonCmd") + " " + _actionString + ".";
968                    break;
969                case SET_TRAIN_ID:
970                case SET_TRAIN_NAME:
971                    str = str + ", \"" + _actionString + "\" " + rbx.getString("onWarrant")
972                            + " \"" + _deviceName + "\".";
973                    break;
974                case SET_BLOCK_VALUE:
975                    str = str + ", \"" + _actionString + "\" " + rbx.getString("onBlock")
976                            + " \"" + _deviceName + "\".";
977                    break;
978                default:
979                    break; // nothing needed for others
980            }
981        }
982        switch (_type) {
983            case SET_LIGHT_INTENSITY:
984            case SET_LIGHT_TRANSITION_TIME:
985                str = str + " " + rbx.getString("to") + " " + _actionData + ".";
986                break;
987            case SET_FAST_CLOCK_TIME:
988                str = str + " " + rbx.getString("to") + " "
989                        + LogixTableAction.formatTime(_actionData / 60, _actionData - ((_actionData / 60) * 60));
990                break;
991            default:
992                break; // nothing needed for others
993        }
994        return str;
995    }
996
997    /** {@inheritDoc} */
998    @Override
999    public void dispose() {
1000        if (_sound != null) {
1001            _sound.dispose();
1002        }
1003    }
1004
1005    private final static Logger log = LoggerFactory.getLogger(DefaultConditionalAction.class);
1006
1007}