001package jmri.jmrix.loconet.locoio;
002
003import java.util.Vector;
004import jmri.jmrix.loconet.LnConstants;
005
006/**
007 * Manage the set of valid modes for a particular LocoIO port,
008 * as well as the conversions between addresses and SV values.
009 * Used in LocoIO tool.
010 * Marked Legacy/Deprecated since 2017 version 4.12.
011 * @author John Plocher, January 30, 2007
012 */
013public class LocoIOModeList {
014
015    protected Vector<LocoIOMode> modeList = new Vector<LocoIOMode>();
016    protected String[] validmodes;
017
018    /**
019     * Create a new instance of LocoIOModeList
020     */
021    public LocoIOModeList() {
022
023        /*
024         * Initialize various configuration modes.
025         * @TODO: Need to tag these with which firmware rev supports
026         * them and only allow choices that match.
027         *
028         * Inputs...
029         */
030        modeList.add(new LocoIOMode(0, LnConstants.OPC_SW_REQ, 0x0F, 0x00, "Toggle Switch, LocoIO 1.3.2"));
031
032        modeList.add(new LocoIOMode(0, LnConstants.OPC_INPUT_REP, 0x5F, 0x00, "Block Detector, Active High"));
033        modeList.add(new LocoIOMode(0, LnConstants.OPC_INPUT_REP, 0x1F, 0x10, "Block Detector, Active Low"));
034        modeList.add(new LocoIOMode(0, LnConstants.OPC_SW_REQ, 0x0F, 0x10, "Toggle Switch, Direct Control"));
035        modeList.add(new LocoIOMode(0, LnConstants.OPC_SW_REP, 0x07, 0x10, "Toggle Switch, Indirect Control"));
036        modeList.add(new LocoIOMode(0, LnConstants.OPC_SW_REQ, 0x6F, 0x00, "Push Button, Active High, Direct Control"));
037        modeList.add(new LocoIOMode(0, LnConstants.OPC_SW_REP, 0x67, 0x00, "Push Button, Active High, Indirect Control"));
038        modeList.add(new LocoIOMode(0, LnConstants.OPC_SW_REQ, 0x2F, 0x10, "Push Button, Active Low, Direct Control"));
039        modeList.add(new LocoIOMode(0, LnConstants.OPC_SW_REP, 0x27, 0x10, "Push Button, Active Low, Indirect Control"));
040        modeList.add(new LocoIOMode(0, LnConstants.OPC_SW_REP, 0x17, 0x70, "Turnout Feedback, single sensor"));
041        modeList.add(new LocoIOMode(0, LnConstants.OPC_SW_REP, 0x37, 0x70, "Turnout Feedback, dual sensor, #1"));
042        modeList.add(new LocoIOMode(0, LnConstants.OPC_SW_REP, 0x37, 0x60, "Turnout Feedback, dual sensor, #2"));
043        /*
044         * and Outputs...
045         */
046        modeList.add(new LocoIOMode(1, LnConstants.OPC_INPUT_REP, 0xC0, 0x00, "Block Occupied Indication"));
047        modeList.add(new LocoIOMode(1, LnConstants.OPC_INPUT_REP, 0xD0, 0x00, "Block Occupied Indication, Blinking"));
048        modeList.add(new LocoIOMode(1, LnConstants.OPC_SW_REQ, 0x81, 0x10, "Steady State, single output, On at Power up"));
049        modeList.add(new LocoIOMode(1, LnConstants.OPC_SW_REQ, 0x80, 0x10, "Steady State, single output, Off at Power up"));
050        modeList.add(new LocoIOMode(1, LnConstants.OPC_SW_REQ, 0x81, 0x30, "Steady State, paired output, On at Power up"));
051        modeList.add(new LocoIOMode(1, LnConstants.OPC_SW_REQ, 0x80, 0x30, "Steady State, paired output, Off at Power up"));
052        modeList.add(new LocoIOMode(1, LnConstants.OPC_SW_REQ, 0x91, 0x10, "Steady State, single output, On at Power up, Blinking"));
053        modeList.add(new LocoIOMode(1, LnConstants.OPC_SW_REQ, 0x90, 0x10, "Steady State, single output, Off at Power up, Blinking"));
054        modeList.add(new LocoIOMode(1, LnConstants.OPC_SW_REQ, 0x91, 0x30, "Steady State, paired output, On at Power up, Blinking"));
055        modeList.add(new LocoIOMode(1, LnConstants.OPC_SW_REQ, 0x90, 0x30, "Steady State, paired output, Off at Power up, Blinking"));
056        modeList.add(new LocoIOMode(1, LnConstants.OPC_SW_REQ, 0x88, 0x20, "Pulsed, software controlled on time, single output"));
057        modeList.add(new LocoIOMode(1, LnConstants.OPC_SW_REQ, 0x8C, 0x20, "Pulsed, firmware controlled on time, single output"));
058        modeList.add(new LocoIOMode(1, LnConstants.OPC_SW_REQ, 0x88, 0x00, "Pulsed, software controlled on time, paired output"));
059        modeList.add(new LocoIOMode(1, LnConstants.OPC_SW_REQ, 0x8C, 0x00, "Pulsed, firmware controlled on time, paired output"));
060
061        validmodes = new String[modeList.size()];
062        for (int i = 0; i <= modeList.size() - 1; i++) {
063            LocoIOMode m = modeList.elementAt(i);
064            validmodes[i] = m.getFullMode();
065        }
066    }
067
068    protected String[] getValidModes() {
069        return validmodes;
070    }
071
072    protected boolean isValidModeValue(Object value) {
073        if (value instanceof String) {
074            String sValue = (String) value;
075            for (int i = 0; i < validmodes.length; i++) {
076                if (sValue.equals(validmodes[i])) {
077                    return true;
078                }
079            }
080        }
081        return false;
082    }
083
084    protected LocoIOMode getLocoIOModeFor(String s) {
085        for (int i = 0; i <= modeList.size() - 1; i++) {
086            LocoIOMode m = modeList.elementAt(i);
087            String ms = m.getFullMode();
088            if (ms.matches(s)) {
089                return m;
090            }
091        }
092        return null;
093    }
094
095    protected LocoIOMode getLocoIOModeFor(int cv, int v1, int v2) {
096        // v2 &= 0x0F;
097        for (int i = 0; i <= modeList.size() - 1; i++) {
098            LocoIOMode m = modeList.elementAt(i);
099            if (m.getSV() == cv) {
100                if ((m.getOpCode() == LnConstants.OPC_INPUT_REP)
101                        && (m.getV2() == (v2 & 0xD0))) {
102                    return m;
103                } else if (((cv == 0x6F) || (cv == 0x67) || (cv == 0x2F) || (cv == 0x27))
104                        && (m.getV2() == (v2 & 0x50))) {
105                    return m;
106                } else if ((m.getV2() == (v2 & 0xB0))) {
107                    return m;
108                } else if (((cv & 0x90) == 0x10)
109                        && ((cv & 0x80) == 0)
110                        && (m.getV2() == (v2 & 0x70))) {
111                    return m;
112                }
113            }
114        }
115        return null;
116    }
117
118    /**
119     * Convert Value1 (Low bits) from Port Address.
120     *
121     * @param lim one of a list of defined port operation modes
122     * @param address the address for this port
123     * @return low-bits value
124     */
125    protected int addressToValue1(LocoIOMode lim, int address) {
126        if (lim == null) {
127            return 0;
128        }
129        return addressToValues(lim.getOpCode(), lim.getSV(), lim.getV2(), address) & 0x7F;
130    }
131
132    /**
133     * Convert Value2 (High bits) from Port Address.
134     *
135     * @param lim one of a list of defined port operation modes
136     * @param address the address for this port
137     * @return high-bits value
138     */
139    protected int addressToValue2(LocoIOMode lim, int address) {
140        if (lim == null) {
141            return 0;
142        }
143        return (addressToValues(lim.getOpCode(), lim.getSV(), lim.getV2(), address) / 256) & 0x7F;
144    }
145
146    /**
147     * Convert bytes from LocoNet packet into a 1-based address for a sensor or
148     * turnout.
149     *
150     * @param a1 Byte containing the upper bits
151     * @param a2 Byte containing the lower bits
152     * @return 1-4096 address as decimal
153     */
154    static private int SENSOR_ADR(int a1, int a2) {
155        return (((a2 & 0x0f) * 128) + (a1 & 0x7f)) + 1;
156    }
157
158    /**
159     * Create 2 byte value from Port Address bits.
160     *
161     * @param opcode coded value for message type
162     * @param sv index of SV value to create, ignored
163     * @param v2mask mask to apply on Value2
164     * @param address the address for this port
165     * @return 2-byte value
166     */
167    protected int addressToValues(int opcode, int sv, int v2mask, int address) {
168        int v1 = 0;
169        int v2 = 0;
170
171        address--;
172
173        if (opcode == LnConstants.OPC_INPUT_REP) {
174            v1 = ((address / 2) & 0x7F);
175            v2 = ((address / 256) & 0x0F);
176            if ((address & 0x01) == 0x01) {
177                v2 |= LnConstants.OPC_INPUT_REP_SW;
178            }
179            v2 |= v2mask;
180        } else if (opcode == LnConstants.OPC_SW_REQ) {
181            v1 = (address & 0x7F);
182            v2 = (address / 128) & 0x0F;
183            v2 &= ~(0x40);
184            v2 |= v2mask;
185        } else if (opcode == LnConstants.OPC_SW_REP) {
186            v1 = (address & 0x7F);
187            v2 = (address / 128) & 0x0F;
188            v2 &= ~(0x40);
189            v2 |= v2mask;
190        }
191        return v2 * 256 + v1;
192    }
193
194    /**
195     * Extract Port Address from the 3 SV values.
196     *
197     * @param opcode coded value for message type
198     * @param sv first SV value, ignored
199     * @param v1 second value (upper bits)
200     * @param v2 second value (lower bits)
201     * @return address (int) of the port
202     */
203    protected int valuesToAddress(int opcode, int sv, int v1, int v2) {
204        //int hi = 0;
205        //int lo = 0;
206        if (opcode == LnConstants.OPC_INPUT_REP) {  /* return 1-4096 address */
207
208            return ((SENSOR_ADR(v1, v2) - 1) * 2 + ((v2 & LnConstants.OPC_INPUT_REP_SW) != 0 ? 2 : 1));
209        } else if (opcode == LnConstants.OPC_SW_REQ) {
210            // if ( ((v2 & 0xCF) == 0x0F)  && ((v1 & 0xFC) == 0x78) ) { // broadcast address LPU V1.0 page 12
211            // "Request Switch to broadcast address with bits "+
212            // "a="+ ((sw2&0x20)>>5)+((sw2 & LnConstants.OPC_SW_REQ_DIR)!=0 ? " (Closed)" : " (Thrown)")+
213            // " c="+ ((sw1 & 0x02)>>1) +
214            // " b="+ ((sw1 & 0x01)) +
215            // "\n\tOutput "+
216            // ((sw2 & LnConstants.OPC_SW_REQ_OUT)!=0 ? "On"     : "Off")+"\n";
217            // } else if ( ((v2 & 0xCF) == 0x07)  && ((v1 & 0xFC) == 0x78) ) { // broadcast address LPU V1.0 page 13
218            // "Request switch command is Interrogate LocoNet with bits "+
219            // "a="+ ((sw2 & 0x20)>>5) +
220            // " c="+ ((sw1&0x02)>>1) +
221            // " b="+ ((sw1&0x01)) +
222            // "\n\tOutput "+
223            // ((sw2 & LnConstants.OPC_SW_REQ_OUT)!=0 ? "On"     : "Off")+"\n"+
224            // ( ( (sw2&0x10) == 0 ) ? "" : "\tNote 0x10 bit in sw2 is unexpectedly 0\n");
225            // } else { // normal command
226            return (SENSOR_ADR(v1, v2));
227            //}
228        } else if (opcode == LnConstants.OPC_SW_REP) {
229            return (SENSOR_ADR(v1, v2));
230        }
231        return -1;
232    }
233
234    protected int valuesToAddress(LocoIOMode lim, int sv, int v1, int v2) {
235        if (lim == null) {
236            return 0;
237        }
238        return valuesToAddress(lim.getOpCode(), sv, v1, v2);
239    }
240
241    // private final static Logger log = LoggerFactory.getLogger(LocoIOModeList.class);
242
243}