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        theMastType = mastSubType;
052        configureFromName(sys);
053    }
054
055    private String theMastType = "F$dsm";
056    private boolean useAddressOffSet = false;
057
058    protected void configureFromName(String systemName) {
059        // split out the basic information
060        String[] parts = systemName.split(":");
061        if (parts.length < 3) {
062            log.error("SignalMast system name needs at least three parts: {}", systemName);
063            throw new IllegalArgumentException("System name needs at least three parts: " + systemName);
064        }
065        if (!parts[0].endsWith(theMastType)) {
066            log.warn("First part of SignalMast system name is incorrect {} : {}", systemName, theMastType);
067        } else {
068            String commandStationPrefix = parts[0].substring(0, parts[0].indexOf("$") - 1);
069            java.util.List<jmri.CommandStation> connList = jmri.InstanceManager.getList(jmri.CommandStation.class);
070
071            for (jmri.CommandStation station : connList) {
072                log.trace(" check against {} with letter {}", station, station.getSystemPrefix());
073                if (station.getSystemPrefix().equals(commandStationPrefix)) {
074                    c = station;
075                    break;
076                }
077            }
078
079            if (c == null) {
080                c = InstanceManager.getNullableDefault(CommandStation.class);
081                log.error("No match against the command station for \"{}\", so will use the default {}", commandStationPrefix, c);
082            }
083        }
084        String system = parts[1];
085        String mast = parts[2];
086
087        mast = mast.substring(0, mast.indexOf("("));
088        log.trace("In configureFromName setMastType to {}", mast);
089        setMastType(mast);
090
091        String tmp = parts[2].substring(parts[2].indexOf("(") + 1, parts[2].indexOf(")"));
092        try {
093            dccSignalDecoderAddress = Integer.parseInt(tmp);
094        } catch (NumberFormatException e) {
095            log.warn("DCC accessory address SystemName {} is not in the correct format", systemName);
096        }
097        configureSignalSystemDefinition(system);
098        configureAspectTable(system, mast);
099    }
100
101    protected HashMap<String, Integer> appearanceToOutput = new HashMap<>();
102
103    public void setOutputForAppearance(String appearance, int number) {
104        if (appearanceToOutput.containsKey(appearance)) {
105            log.debug("Appearance {} is already defined as {}", appearance, appearanceToOutput.get(appearance));
106            appearanceToOutput.remove(appearance);
107        }
108        appearanceToOutput.put(appearance, number);
109    }
110
111    public int getOutputForAppearance(String appearance) {
112        if (!appearanceToOutput.containsKey(appearance)) {
113            log.error("Trying to get appearance {} but it has not been configured", appearance);
114            return -1;
115        }
116        return appearanceToOutput.get(appearance);
117    }
118
119    /*
120     0.  "Stop"
121     1.  "Take Siding"
122     2.  "Stop-Orders"
123     3.  "Stop-Proceed"
124     4.  "Restricting"
125     5.  "Permissive"
126     6.  "Slow-Approach"
127     7.  "Slow"
128     8.  "Slow-Medium"
129     9.  "Slow-Limited"
130     10. "Slow-Clear"
131     11. "Medium-Approach"
132     12. "Medium-Slow"
133     13. "Medium"
134     14. "Medium-Ltd"
135     15. "Medium-Clr"
136     16. "Limited-Approach"
137     17. "Limited-Slow"
138     18. "Limited-Med"
139     19. "Limited"
140     20. "Limited-Clear"
141     21. "Approach"
142     22. "Advance-Appr"
143     23. "Appr-Slow"
144     24. "Adv-Appr-Slow"
145     25. "Appr-Medium"
146     26. "Adv-Appr-Med"
147     27. "Appr-Limited"
148     28. "Adv-Appr-Ltd"
149     29. "Clear"
150     30. "Cab-Speed"
151     31. "Dark" */
152    protected int packetSendCount = 3;  // default 3
153
154    @Override
155    public void setAspect(@Nonnull String aspect) {
156        if (appearanceToOutput.containsKey(aspect) && appearanceToOutput.get(aspect) != -1) {
157            if (getLit()) {
158                if (useAddressOffSet) {
159                    c.sendPacket(NmraPacket.accSignalDecoderPkt(dccSignalDecoderAddress, appearanceToOutput.get(aspect)), packetSendCount);
160                } else {
161                    c.sendPacket(NmraPacket.altAccSignalDecoderPkt(dccSignalDecoderAddress, appearanceToOutput.get(aspect)), packetSendCount);
162                }
163            }
164        } else {
165            log.warn("Trying to set aspect ({}) that has not been configured on mast {}", aspect, getDisplayName());
166        }
167        super.setAspect(aspect);
168    }
169
170
171    public void useAddressOffSet(boolean boo) {
172        useAddressOffSet = boo;
173    }
174
175    public boolean useAddressOffSet() {
176        return useAddressOffSet;
177    }
178
179    @Override
180    public void setLit(boolean newLit) {
181        if (!allowUnLit() || newLit == getLit()) {
182            return;
183        }
184        super.setLit(newLit);
185        if (newLit) {
186            String newAspect = getAspect();
187            if (newAspect != null){
188                setAspect(newAspect);
189            }
190        } else {
191            if (useAddressOffSet) {
192                c.sendPacket(NmraPacket.accSignalDecoderPkt(dccSignalDecoderAddress, unLitId), packetSendCount);
193            } else {
194                c.sendPacket(NmraPacket.altAccSignalDecoderPkt(dccSignalDecoderAddress, unLitId), packetSendCount);
195            }
196        }
197    }
198
199    int unLitId = 31;
200
201    public void setUnlitId(int i) {
202        unLitId = i;
203    }
204
205    public int getUnlitId() {
206        return unLitId;
207    }
208
209    public int getDccSignalMastAddress() {
210        return dccSignalDecoderAddress;
211    }
212
213    public CommandStation getCommandStation() {
214        return c;
215    }
216
217    protected CommandStation c;
218
219    protected int dccSignalDecoderAddress;
220
221    public static String isDCCAddressUsed(int addr) {
222        for (SignalMast mast : InstanceManager.getDefault(jmri.SignalMastManager.class).getNamedBeanSet()) {
223            if (mast instanceof jmri.implementation.DccSignalMast) {
224                if (((DccSignalMast) mast).getDccSignalMastAddress() == addr) {
225                    return ((DccSignalMast) mast).getDisplayName();
226                }
227            }
228        }
229        return null;
230    }
231
232    /**
233     * Set Number of times the packet should be sent.
234     * @param count - less than 1 is treated as 1.
235     */
236    public void setDccSignalMastPacketSendCount(int count) {
237        if (count >= 0) {
238            packetSendCount = count;
239        } else {
240            packetSendCount = 1;
241        }
242    }
243
244    /**
245     * Get the number of times the packet should be sent to the track.
246     *
247     * @return the count.
248     */
249    public int getDccSignalMastPacketSendCount() {
250        return packetSendCount;
251    }
252
253    private final static Logger log = LoggerFactory.getLogger(DccSignalMast.class);
254
255}