001package jmri.implementation;
002
003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
004import java.util.Arrays;
005import jmri.NamedBeanHandle;
006import jmri.Turnout;
007import org.slf4j.Logger;
008import org.slf4j.LoggerFactory;
009
010/**
011 * Drive a single searchlight signal head via three "Turnout" objects.
012 * <p>
013 * "Triple Output RGB" to differentiate from the existing RYG triple output
014 * head; The class name fits in with the quad output name which is the
015 * equivalent discrete lamp head.
016 * <p>
017 * The three Turnout objects are provided during construction, and each drives a
018 * specific color (RED, GREEN and BLUE). Normally, "THROWN" is on, and "CLOSED"
019 * is off.
020 * <p>
021 * Red = Red Green = Green Yellow = Red and Green Lunar = Red, Green and Blue
022 * <p>
023 * This class doesn't currently listen to the Turnout's to see if they've been
024 * changed via some other mechanism.
025 *
026 * @author Suzie Tall based on Bob Jacobsen's work
027 * @author Bob Jacobsen Copyright (C) 2003, 2008
028 */
029public class TripleOutputSignalHead extends DoubleTurnoutSignalHead {
030    public TripleOutputSignalHead(String sys, String user, NamedBeanHandle<Turnout> green, NamedBeanHandle<Turnout> blue, NamedBeanHandle<Turnout> red) {
031        super(sys, user, green, red);
032        mBlue = blue;
033    }
034
035    public TripleOutputSignalHead(String sys, NamedBeanHandle<Turnout> green, NamedBeanHandle<Turnout> blue, NamedBeanHandle<Turnout> red) {
036        super(sys, green, red);
037        mBlue = blue;
038    }
039
040    @SuppressWarnings("fallthrough")
041    @SuppressFBWarnings(value = "SF_SWITCH_FALLTHROUGH")
042    @Override
043    protected void updateOutput() {
044        // assumes that writing a turnout to an existing state is cheap!
045        if (!mLit) {
046            mRed.getBean().setCommandedState(Turnout.CLOSED);
047            mBlue.getBean().setCommandedState(Turnout.CLOSED);
048            mGreen.getBean().setCommandedState(Turnout.CLOSED);
049        } else if (!mFlashOn
050                && ((mAppearance == FLASHGREEN)
051                || (mAppearance == FLASHYELLOW)
052                || (mAppearance == FLASHLUNAR)
053                || (mAppearance == FLASHRED))) {
054            // flash says to make output dark
055            mRed.getBean().setCommandedState(Turnout.CLOSED);
056            mBlue.getBean().setCommandedState(Turnout.CLOSED);
057            mGreen.getBean().setCommandedState(Turnout.CLOSED);
058        } else {
059            switch (mAppearance) {
060                case RED:
061                case FLASHRED:
062                    mRed.getBean().setCommandedState(Turnout.THROWN);
063                    mBlue.getBean().setCommandedState(Turnout.CLOSED);
064                    mGreen.getBean().setCommandedState(Turnout.CLOSED);
065                    break;
066                case YELLOW:
067                case FLASHYELLOW:
068                    mRed.getBean().setCommandedState(Turnout.THROWN);
069                    mBlue.getBean().setCommandedState(Turnout.CLOSED);
070                    mGreen.getBean().setCommandedState(Turnout.THROWN);
071                    break;
072                case GREEN:
073                case FLASHGREEN:
074                    mRed.getBean().setCommandedState(Turnout.CLOSED);
075                    mBlue.getBean().setCommandedState(Turnout.CLOSED);
076                    mGreen.getBean().setCommandedState(Turnout.THROWN);
077                    break;
078                case LUNAR:
079                case FLASHLUNAR:
080                    mRed.getBean().setCommandedState(Turnout.THROWN);
081                    mBlue.getBean().setCommandedState(Turnout.THROWN);
082                    mGreen.getBean().setCommandedState(Turnout.THROWN);
083                    break;
084                default:
085                    log.warn("Unexpected new appearance: {}", mAppearance);
086                // go dark by falling through
087                case DARK:
088                    mRed.getBean().setCommandedState(Turnout.CLOSED);
089                    mBlue.getBean().setCommandedState(Turnout.CLOSED);
090                    mGreen.getBean().setCommandedState(Turnout.CLOSED);
091                    break;
092            }
093        }
094    }
095
096    /**
097     * Remove references to and from this object, so that it can eventually be
098     * garbage-collected.
099     */
100    @Override
101    public void dispose() {
102        mBlue = null;
103        super.dispose();
104    }
105
106    private NamedBeanHandle<Turnout> mBlue;
107
108    public NamedBeanHandle<Turnout> getBlue() {
109        return mBlue;
110    }
111
112    public void setBlue(NamedBeanHandle<Turnout> t) {
113        mBlue = t;
114    }
115
116    // claim support for Lunar aspects
117    private final static int[] validStates = new int[]{
118        DARK,
119        RED,
120        LUNAR,
121        YELLOW,
122        GREEN,
123        FLASHRED,
124        FLASHLUNAR,
125        FLASHYELLOW,
126        FLASHGREEN
127    };
128    private static final String[] validStateKeys = new String[]{
129        "SignalHeadStateDark",
130        "SignalHeadStateRed",
131        "SignalHeadStateLunar",
132        "SignalHeadStateYellow",
133        "SignalHeadStateGreen",
134        "SignalHeadStateFlashingRed",
135        "SignalHeadStateFlashingLunar",
136        "SignalHeadStateFlashingYellow",
137        "SignalHeadStateFlashingGreen"
138    };
139
140    /**
141     * {@inheritDoc}
142     */
143    @Override
144    public int[] getValidStates() {
145        return Arrays.copyOf(validStates, validStates.length);
146    }
147
148    /**
149     * {@inheritDoc}
150     */
151    @Override
152    public String[] getValidStateKeys() {
153        return Arrays.copyOf(validStateKeys, validStateKeys.length); // includes int for Lunar
154    }
155
156    /**
157     * {@inheritDoc}
158     */
159    @Override
160    public String[] getValidStateNames() {
161        String[] stateNames = new String[validStateKeys.length];
162        int i = 0;
163        for (String stateKey : validStateKeys) {
164            stateNames[i++] = Bundle.getMessage(stateKey);
165        }
166        return stateNames;
167    }
168
169    @Override
170    boolean isTurnoutUsed(Turnout t) {
171        if (super.isTurnoutUsed(t)) {
172            return true;
173        }
174        if (getBlue() != null && t.equals(getBlue().getBean())) {
175            return true;
176        }
177        return false;
178    }
179
180    /**
181     * Disables the feedback mechanism of the DoubleTurnoutSignalHead.
182     */
183    @Override
184    void readOutput() { }
185
186    private final static Logger log = LoggerFactory.getLogger(TripleOutputSignalHead.class);
187
188}