001package jmri;
002
003import java.util.EnumSet;
004
005/**
006 * DCC Speed Step Mode.
007 *
008 * <hr>
009 * This file is part of JMRI.
010 * <p>
011 * JMRI is free software; you can redistribute it and/or modify it under the
012 * terms of version 2 of the GNU General Public License as published by the Free
013 * Software Foundation. See the "COPYING" file for a copy of this license.
014 * <p>
015 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY
016 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
017 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
018 *
019 * @author Austin Hendrix Copyright (C) 2019
020  * with edits/additions by
021 * @author Timothy Jump Copyright (C) 2025
022 */
023@javax.annotation.concurrent.Immutable
024public enum SpeedStepMode {
025    // NOTE: keep these up to date with xml/schema/locomotive-config.xsd
026    UNKNOWN("unknown", 1, 0.0f, "SpeedStepUnknown"),
027    // NMRA DCC standard speed step modes.
028    NMRA_DCC_128("128", 126, "SpeedStep128"), // Remember there are only 126 non-stop values in 128 speed.
029    NMRA_DCC_28("28", 28, "SpeedStep28"),
030    NMRA_DCC_27("27", 27, "SpeedStep27"),
031    NMRA_DCC_14("14", 14, "SpeedStep14"),
032    // Non-DCC speed step modes.
033    MOTOROLA_28("motorola_28", 28, "SpeedStep28Motorola"), // Motorola 28 speed step mode.
034    TMCC1_32("tmcc1_32", 32, "SpeedStep32TMCC1"), // Lionel TMCC 1, 32 speed step mode.
035    TMCC2_32("tmcc2_32", 32, "SpeedStep32TMCC2"), // Lionel TMCC 2 Legacy, 32 speed step mode.
036    TMCC1_100("tmcc1_100", 100, "SpeedStep100TMCC1"), // Lionel TMCC 1, 100 "relative" speed step mode.
037    TMCC2_200("tmcc2_200", 200, "SpeedStep200TMCC2"), // Lionel TMCC 2 Legacy, 200 speed step mode.
038    TMCC1TR_32("tmcc1tr_32", 32, "SpeedStep32TMCC1TR"), // Lionel TMCC 1 TR, 32 speed step mode.
039    TMCC2TR_32("tmcc2tr_32", 32, "SpeedStep32TMCC2TR"), // Lionel TMCC 2 Legacy TR, 32 speed step mode.
040    TMCC1TR_100("tmcc1tr_100", 100, "SpeedStep100TMCC1TR"), // Lionel TMCC 1 TR, 100 "relative" speed step mode.
041    TMCC2TR_200("tmcc2tr_200", 200, "SpeedStep200TMCC2TR"), // Lionel TMCC 2 Legacy TR, 200 speed step mode.
042    INCREMENTAL("incremental", 1, 1.0f, "SpeedStepIncremental");
043
044    SpeedStepMode(String name, int numSteps, String description) {
045        this(name, numSteps, 1.0f / numSteps, description);
046    }
047
048    SpeedStepMode(String name, int numSteps, float increment, String description) {
049        this.name = name;
050        this.numSteps = numSteps;
051        this.increment = increment;
052        this.description = Bundle.getMessage(description);
053    }
054
055    public final String name;
056
057    /**
058     * The Number of steps, e.g. 126 for DCC 128
059     */
060    public final int numSteps;
061
062     /**
063     * The increment between steps, e.g. 1 / 126 for DCC 128
064     */
065    public final float increment;
066    public final String description;
067
068    /**
069     * Get a locale friendly Step Mode Description.
070     * For just "128" use name()
071     * @return e.g. "128 SS"
072     */
073    @Override
074    public String toString() {
075        return description;
076    }
077
078    /**
079     * Convert a human-readable string to a DCC speed step mode.
080     *
081     * @param name string version of speed step mode; example "128"
082     * @return matching SpeedStepMode
083     * @throws IllegalArgumentException if name does not correspond to a valid speed step mode.
084     */
085    static public SpeedStepMode getByName(String name) {
086        for (SpeedStepMode s : SpeedStepMode.values()) {
087            if (s.name.equals(name)) {
088                return s;
089            }
090        }
091        throw new IllegalArgumentException("Invalid speed step mode: " + name);
092    }
093
094    /**
095     * Convert a localized name string to a DCC speed step mode.
096     *
097     * @param name localized string version of speed step mode; example "128"
098     * @return matching SpeedStepMode
099     * @throws IllegalArgumentException if name does not correspond to a valid speed step mode.
100     */
101    static public SpeedStepMode getByDescription(String name) {
102        for (SpeedStepMode s : SpeedStepMode.values()) {
103            if (s.description.equals(name)) {
104                return s;
105            }
106        }
107        throw new IllegalArgumentException("Invalid speed step mode: " + name);
108    }
109
110    static public EnumSet<SpeedStepMode> getCompatibleModes(
111            EnumSet<SpeedStepMode> command_station_modes,
112            EnumSet<SpeedStepMode> decoder_modes) {
113        EnumSet<SpeedStepMode> result = command_station_modes.clone();
114        result.retainAll(decoder_modes);
115        return result;
116    }
117
118    static public SpeedStepMode bestCompatibleMode(
119            EnumSet<SpeedStepMode> command_station_modes,
120            EnumSet<SpeedStepMode> decoder_modes) {
121        EnumSet<SpeedStepMode> result = getCompatibleModes(command_station_modes, decoder_modes);
122        return bestMode(result);
123    }
124
125    static public SpeedStepMode bestMode(EnumSet<SpeedStepMode> modes) {
126        if(modes.contains(NMRA_DCC_128)) {
127            return NMRA_DCC_128;
128        } else if(modes.contains(TMCC1_32)) {
129            return TMCC1_32;
130        } else if(modes.contains(NMRA_DCC_28)) {
131            return NMRA_DCC_28;
132        } else if(modes.contains(MOTOROLA_28)) {
133            return MOTOROLA_28;
134        } else if(modes.contains(NMRA_DCC_27)) {
135            return NMRA_DCC_27;
136        } else if(modes.contains(NMRA_DCC_14)) {
137            return NMRA_DCC_14;
138        }
139        return UNKNOWN;
140    }
141
142    static public EnumSet<SpeedStepMode> getCompatibleModesForProtocol(LocoAddress.Protocol protocol) {
143        switch (protocol) {
144            case DCC:
145            case DCC_LONG:
146            case DCC_SHORT:
147                return EnumSet.of(
148                        // NMRA Speed step modes.
149                        SpeedStepMode.NMRA_DCC_128,
150                        SpeedStepMode.NMRA_DCC_28,
151                        SpeedStepMode.NMRA_DCC_27,
152                        SpeedStepMode.NMRA_DCC_14,
153                        // Incremental speed step mode, used by LENZ XPA
154                        // XpressNet Phone Adapter.
155                        SpeedStepMode.INCREMENTAL,
156                        // TMCC1 mode, since some NMRA decoder models are used
157                        // for TMCC1 locomotives.
158                        SpeedStepMode.TMCC1_32);
159            case MFX:
160                return EnumSet.of(
161                        // NMRA Speed step modes.
162                        SpeedStepMode.NMRA_DCC_128,
163                        SpeedStepMode.NMRA_DCC_28,
164                        SpeedStepMode.NMRA_DCC_27,
165                        SpeedStepMode.NMRA_DCC_14,
166                        // Incremental speed step mode, used by LENZ XPA
167                        // XpressNet Phone Adapter.
168                        SpeedStepMode.INCREMENTAL,
169                        // MFX decoders also support Motorola speed step mode.
170                        SpeedStepMode.MOTOROLA_28);
171            case MOTOROLA:
172                return EnumSet.of(SpeedStepMode.MOTOROLA_28);
173            case SELECTRIX:
174            case M4:
175            case OPENLCB:
176            case LGB:
177                // No compatible speed step modes for these protocols.
178                // NOTE: these protocols only appear to be used in conjunction
179                // with ECOS.
180                break;
181            default:
182                // Unhandled case; no compatible speed step mode.
183                break;
184        }
185        return EnumSet.noneOf(SpeedStepMode.class);
186    }
187}