001package jmri.implementation;
002
003import java.util.HashMap;
004import javax.annotation.Nonnull;
005import jmri.CommandStation;
006import jmri.InstanceManager;
007import jmri.NmraPacket;
008import jmri.SignalMast;
009import org.slf4j.Logger;
010import org.slf4j.LoggerFactory;
011
012/**
013 * This class implements a SignalMast that uses <b>Extended Accessory Decoder
014 * Control Packet Format</b>
015 * and outputs that packet to the DCC System via the generic CommandStation
016 * interface.
017 * <p>
018 * This implementation writes out to the physical signal when it's commanded to
019 * change appearance, and updates its internal state when it hears commands from
020 * other places.
021 * <p>
022 * System name specifies the creation information:
023 * <pre>
024 * IF$dsm:basic:one-searchlight(123)
025 * </pre> The name is a colon-separated series of terms:
026 * <ul>
027 *   <li>IF$dsm - defines signal masts of this type
028 *   <li>basic - name of the signaling system
029 *   <li>one-searchlight - name of the particular aspect map
030 *   <li>(123) - DCC address for the decoder
031 * </ul>
032 * <p>
033 * Based upon {@link jmri.implementation.DccSignalHead} by Alex Shepherd
034 *
035 * @author Kevin Dickerson Copyright (c) 2012
036 */
037public class DccSignalMast extends AbstractSignalMast {
038
039    public DccSignalMast(String sys, String user) {
040        super(sys, user);
041        configureFromName(sys);
042    }
043
044    public DccSignalMast(String sys) {
045        super(sys);
046        configureFromName(sys);
047    }
048
049    public DccSignalMast(String sys, String user, String mastSubType) {
050        super(sys, user);
051        mastType = mastSubType;
052        configureFromName(sys);
053    }
054
055    private String mastType = "F$dsm";
056
057    protected void configureFromName(String systemName) {
058        // split out the basic information
059        String[] parts = systemName.split(":");
060        if (parts.length < 3) {
061            log.error("SignalMast system name needs at least three parts: {}", systemName);
062            throw new IllegalArgumentException("System name needs at least three parts: " + systemName);
063        }
064        if (!parts[0].endsWith(mastType)) {
065            log.warn("First part of SignalMast system name is incorrect {} : {}", systemName, mastType);
066        } else {
067            String commandStationPrefix = parts[0].substring(0, parts[0].indexOf("$") - 1);
068            java.util.List<jmri.CommandStation> connList = jmri.InstanceManager.getList(jmri.CommandStation.class);
069
070            for (jmri.CommandStation station : connList) {
071                log.trace(" check against {} with letter {}", station, station.getSystemPrefix());
072                if (station.getSystemPrefix().equals(commandStationPrefix)) {
073                    c = station;
074                    break;
075                }
076            }
077
078            if (c == null) {
079                c = InstanceManager.getNullableDefault(CommandStation.class);
080                log.error("No match against the command station for \"{}\", so will use the default {}", commandStationPrefix, c);
081            }
082        }
083        String system = parts[1];
084        String mast = parts[2];
085
086        mast = mast.substring(0, mast.indexOf("("));
087        log.trace("In configureFromName setMastType to {}", mast);
088        setMastType(mast);
089        
090        String tmp = parts[2].substring(parts[2].indexOf("(") + 1, parts[2].indexOf(")"));
091        try {
092            dccSignalDecoderAddress = Integer.parseInt(tmp);
093        } catch (NumberFormatException e) {
094            log.warn("DCC accessory address SystemName {} is not in the correct format", systemName);
095        }
096        configureSignalSystemDefinition(system);
097        configureAspectTable(system, mast);
098    }
099
100    protected HashMap<String, Integer> appearanceToOutput = new HashMap<String, Integer>();
101
102    public void setOutputForAppearance(String appearance, int number) {
103        if (appearanceToOutput.containsKey(appearance)) {
104            log.debug("Appearance {} is already defined as {}", appearance, appearanceToOutput.get(appearance));
105            appearanceToOutput.remove(appearance);
106        }
107        appearanceToOutput.put(appearance, number);
108    }
109
110    public int getOutputForAppearance(String appearance) {
111        if (!appearanceToOutput.containsKey(appearance)) {
112            log.error("Trying to get appearance {} but it has not been configured", appearance);
113            return -1;
114        }
115        return appearanceToOutput.get(appearance);
116    }
117
118    /*
119     0.  "Stop"
120     1.  "Take Siding"
121     2.  "Stop-Orders"
122     3.  "Stop-Proceed"
123     4.  "Restricting"
124     5.  "Permissive"
125     6.  "Slow-Approach"
126     7.  "Slow"
127     8.  "Slow-Medium"
128     9.  "Slow-Limited"
129     10. "Slow-Clear"
130     11. "Medium-Approach"
131     12. "Medium-Slow"
132     13. "Medium"
133     14. "Medium-Ltd"
134     15. "Medium-Clr"
135     16. "Limited-Approach"
136     17. "Limited-Slow"
137     18. "Limited-Med"
138     19. "Limited"
139     20. "Limited-Clear"
140     21. "Approach"
141     22. "Advance-Appr"
142     23. "Appr-Slow"
143     24. "Adv-Appr-Slow"
144     25. "Appr-Medium"
145     26. "Adv-Appr-Med"
146     27. "Appr-Limited"
147     28. "Adv-Appr-Ltd"
148     29. "Clear"
149     30. "Cab-Speed"
150     31. "Dark" */
151    protected int packetSendCount = 3;  // default 3
152
153    @Override
154    public void setAspect(@Nonnull String aspect) {
155        // if already set do not send again.
156        if ( getAspect() != null && getAspect().equals(aspect) ) {
157            return;
158        }
159        if (appearanceToOutput.containsKey(aspect) && appearanceToOutput.get(aspect) != -1) {
160            c.sendPacket(NmraPacket.altAccSignalDecoderPkt(dccSignalDecoderAddress, appearanceToOutput.get(aspect)), packetSendCount);
161        } else {
162            log.warn("Trying to set aspect ({}) that has not been configured on mast {}", aspect, getDisplayName());
163        }
164        super.setAspect(aspect);
165    }
166
167    @Override
168    public void setLit(boolean newLit) {
169        if (!allowUnLit() || newLit == getLit()) {
170            return;
171        }
172        if (newLit) {
173            setAspect(getAspect());
174        } else {
175            c.sendPacket(NmraPacket.altAccSignalDecoderPkt(dccSignalDecoderAddress, unLitId), packetSendCount);
176        }
177        super.setLit(newLit);
178    }
179
180    int unLitId = 31;
181
182    public void setUnlitId(int i) {
183        unLitId = i;
184    }
185
186    public int getUnlitId() {
187        return unLitId;
188    }
189
190    public int getDccSignalMastAddress() {
191        return dccSignalDecoderAddress;
192    }
193
194    public CommandStation getCommandStation() {
195        return c;
196    }
197
198    protected CommandStation c;
199
200    protected int dccSignalDecoderAddress;
201
202    public static String isDCCAddressUsed(int addr) {
203        for (SignalMast mast : InstanceManager.getDefault(jmri.SignalMastManager.class).getNamedBeanSet()) {
204            if (mast instanceof jmri.implementation.DccSignalMast) {
205                if (((DccSignalMast) mast).getDccSignalMastAddress() == addr) {
206                    return ((DccSignalMast) mast).getDisplayName();
207                }
208            }
209        }
210        return null;
211    }
212
213    /**
214     * Set Number of times the packet should be sent.
215     * @param count - less than 1 is treated as 1.
216     */
217    public void setDccSignalMastPacketSendCount(int count) {
218        if (count >= 0) {
219            packetSendCount = count;
220        } else {
221            packetSendCount = 1;
222        }
223    }
224
225    /**
226     * Get the number of times the packet should be sent to the track.
227     *
228     * @return the count.
229     */
230    public int getDccSignalMastPacketSendCount() {
231        return packetSendCount;
232    }
233
234    private final static Logger log = LoggerFactory.getLogger(DccSignalMast.class);
235
236}