001package jmri.jmrix.tmcc;
002
003import java.util.ArrayList;
004import java.util.List;
005import javax.annotation.Nonnull;
006
007import jmri.AddressedProgrammer;
008import jmri.ProgListener;
009import jmri.ProgrammerException;
010import jmri.ProgrammingMode;
011
012/**
013 * Provide an Ops Mode Programmer via a wrapper that works with the
014 * TMCC control interface
015 * <p>
016 * Functionally, this just creates packets to send via the Command Station.
017 *
018 * @see jmri.Programmer
019 * @author Bob Jacobsen Copyright (C) 2002, 2025
020 * with edits/additions by
021 * @author Timothy Jump Copyright (C) 2025
022 */
023public class TmccOpsModeProgrammer extends TmccProgrammer implements AddressedProgrammer {
024
025    int mAddress;
026    boolean mLongAddr;
027
028    public TmccOpsModeProgrammer(int pAddress, boolean pLongAddr, TmccSystemConnectionMemo memo) {
029        super(memo);
030        mAddress = pAddress;
031        mLongAddr = pLongAddr;
032    }
033
034
035    /** 
036     * {@inheritDoc}
037     */
038    @Override
039    @Nonnull
040    public List<ProgrammingMode> getSupportedModes() {
041        List<ProgrammingMode> ret = new ArrayList<ProgrammingMode>();
042
043        ret.add(TmccProgrammerManager.TMCCMODE1_ENGFEATURE);
044        ret.add(TmccProgrammerManager.TMCCMODE2_ENGFEATURE);
045
046        return ret;
047    }
048
049
050    int _cv; // points to "CV" input from Simple Programmer
051    int _val; // points to "Value" input from Simple Programmer
052
053
054
055    /** 
056     * {@inheritDoc}
057     *
058     * Forward a write request to an ops-mode write operation.
059     */
060    @Override
061    public synchronized void writeCV(String CVname, int val, ProgListener p) throws ProgrammerException {
062        final int CV = Integer.parseInt(CVname);
063        log.debug("write CV={} val={}", CV, val);
064
065
066        _cv = CV; // Value from Simple Programmer "CV" input
067        _val = val; // Value from Simple Programmer "Value" input
068
069
070
071        // validate CV == 2 for TMCC loco Feature programming
072        // validate ID#/address for TMCC is between 1-98
073        // validate Feature Type for TMCC
074        // format and send the TMCC loco Feature write message
075        // note: the argument is long containing 3 bytes 
076
077        if (CV == 2) {
078            
079            if (mAddress > 0 && mAddress < 99) {
080
081                // TMCC2 Feature Types
082                if  (getMode() == TmccProgrammerManager.TMCCMODE2_ENGFEATURE) {
083
084                    if (val == 0) {
085                        SerialMessage m = new SerialMessage();
086                        m.setOpCode(0xF8); // set the first byte/TMCC2 opcode to 0xF8
087                        m.putAsWord(((mAddress * 512) + 256) + 16); // set the second/third byte (address/numeric for TMCC2 val = 0)
088                        tc.sendSerialMessage(m, null);
089
090                    } else if (val == 1) {
091                        SerialMessage m = new SerialMessage();
092                        m.setOpCode(0xF8); // set the first byte/TMCC2 opcode to 0xF8
093                        m.putAsWord(((mAddress * 512) + 256) + 17); // set the second/third byte (address/numeric for TMCC2 val = 1)
094                        tc.sendSerialMessage(m, null);
095
096                    } else if (val == 2) {
097                        SerialMessage m = new SerialMessage();
098                        m.setOpCode(0xF8); // set the first byte/TMCC2 opcode to 0xF8
099                        m.putAsWord(((mAddress * 512) + 256) + 18); // set the second/third byte (address/numeric for TMCC2 val = 2)
100                        tc.sendSerialMessage(m, null);
101
102                    } else {
103                        SerialMessage m = new SerialMessage();
104                        m.setOpCode(0x00);
105                        m.putAsWord(00004);
106                        tc.sendSerialMessage(m, null);
107                        log.warn("Value Entered is Not a TMCC2 Feature Type");
108                    }
109
110                }
111
112
113                // TMCC1 Feature Types
114                if (getMode() == TmccProgrammerManager.TMCCMODE1_ENGFEATURE) {         
115
116                    if (val == 4) {
117                        SerialMessage m = new SerialMessage();
118                        m.setOpCode(0xFE); // set the first byte/TMCC1 opcode to 0xFE
119                        m.putAsWord((mAddress * 128) + 20); // set the second/third byte (address/numeric for TMCC1 val = 4)
120                        tc.sendSerialMessage(m, null);
121
122                    } else if (val == 5) {
123                        SerialMessage m = new SerialMessage();
124                        m.setOpCode(0xFE); // set the first byte/TMCC1 opcode to 0xFE
125                        m.putAsWord((mAddress * 128) + 21); // set the second/third byte (address/numeric for TMCC1 val = 5)
126                        tc.sendSerialMessage(m, null);
127
128                    } else if (val == 6) {
129                        SerialMessage m = new SerialMessage();
130                        m.setOpCode(0xFE); // set the first byte/TMCC1 opcode to 0xFE
131                        m.putAsWord((mAddress * 128) + 22); // set the second/third byte (address/numeric for TMCC1 val = 6)
132                        tc.sendSerialMessage(m, null);
133
134                    } else if (val == 8) {
135                        SerialMessage m = new SerialMessage();
136                        m.setOpCode(0xFE); // set the first byte/TMCC1 opcode to 0xFE
137                        m.putAsWord((mAddress * 128) + 24); // set the second/third byte (address/numeric for TMCC1 val = 8)
138                        tc.sendSerialMessage(m, null);
139
140                    } else if (val == 34) {
141                        SerialMessage m = new SerialMessage();
142                        m.setOpCode(0xFE); // set the first byte/TMCC1 opcode to 0xFE
143                        m.putAsWord((mAddress * 128) + 39); // set the second/third byte (address/numeric for TMCC1 val = 34)
144                        tc.sendSerialMessage(m, null);
145
146                    } else if (val == 36) {
147                        SerialMessage m = new SerialMessage();
148                        m.setOpCode(0xFE); // set the first byte/TMCC1 opcode to 0xFE
149                        m.putAsWord((mAddress * 128) + 41); // set the second/third byte (address/numeric for TMCC1 val = 36)
150                        tc.sendSerialMessage(m, null);
151
152                    } else if (val == 74) {
153                        SerialMessage m = new SerialMessage();
154                        m.setOpCode(0xFE); // set the first byte/TMCC1 opcode to 0xFE
155                        m.putAsWord((mAddress * 128) + 43); // set the second/third byte (address/numeric for TMCC1 val = 74)
156                        tc.sendSerialMessage(m, null);
157
158                    } else if (val == 75) {
159                        SerialMessage m = new SerialMessage();
160                        m.setOpCode(0xFE); // set the first byte/TMCC1 opcode to 0xFE
161                        m.putAsWord((mAddress * 128) + 44); // set the second/third byte (address/numeric for TMCC1 val = 75)
162                        tc.sendSerialMessage(m, null);
163
164                    } else if (val == 76) {
165                        SerialMessage m = new SerialMessage();
166                        m.setOpCode(0xFE); // set the first byte/TMCC1 opcode to 0xFE
167                        m.putAsWord((mAddress * 128) + 45); // set the second/third byte (address/numeric for TMCC1 val = 76)
168                        tc.sendSerialMessage(m, null);
169
170                    } else if (val == 740) {
171                        SerialMessage m = new SerialMessage();
172                        m.setOpCode(0xFE); // set the first byte/TMCC1 opcode to 0xFE
173                        m.putAsWord((mAddress * 128) + 59); // set the second/third byte (address/numeric for TMCC1 val = 740)
174                        tc.sendSerialMessage(m, null);
175
176                    } else if (val == 750) {
177                        SerialMessage m = new SerialMessage();
178                        m.setOpCode(0xFE); // set the first byte/TMCC1 opcode to 0xFE
179                        m.putAsWord((mAddress * 128) + 60); // set the second/third byte (address/numeric for TMCC1 val = 750)
180                        tc.sendSerialMessage(m, null);
181
182                    } else if (val == 760) {
183                        SerialMessage m = new SerialMessage();
184                        m.setOpCode(0xFE); // set the first byte/TMCC1 opcode to 0xFE
185                        m.putAsWord((mAddress * 128) + 61); // set the second/third byte (address/numeric for TMCC1 val = 760)
186                        tc.sendSerialMessage(m, null);
187
188                    } else {
189                        SerialMessage m = new SerialMessage();
190                        m.setOpCode(0x00);
191                        m.putAsWord(00003);
192                        tc.sendSerialMessage(m, null);
193                        log.warn("Value Entered is Not a TMCC1 Feature Type");
194                    }
195
196                }
197
198            } else {
199                SerialMessage m = new SerialMessage();
200                m.setOpCode(0x00);
201                m.putAsWord(00000);
202                tc.sendSerialMessage(m, null);
203                log.warn("Address Must be Between 1-98 for TMCC");
204            }
205
206        } else {
207            SerialMessage m = new SerialMessage();
208            m.setOpCode(0x00);
209            m.putAsWord(00002);
210            tc.sendSerialMessage(m, null);
211            log.warn("CV Must Equal 2 for Programming TMCC Feature Type");
212
213        }
214
215        // End the "writing..." process in SimpleProgrammer
216        notifyProgListenerEnd(p, _val, jmri.ProgListener.OK);
217 
218    }
219
220
221    /** 
222     * {@inheritDoc}
223     */
224    @Override
225    public synchronized void readCV(String CVname, ProgListener p) throws ProgrammerException {
226        final int CV = Integer.parseInt(CVname);
227        log.debug("read CV={}", CV);
228        log.error("readCV not available in this protocol");
229        throw new ProgrammerException();
230    }
231
232    /** 
233     * {@inheritDoc}
234     */
235    @Override
236    public synchronized void confirmCV(String CV, int val, ProgListener p) throws ProgrammerException {
237        log.debug("confirm CV={}", CV);
238        log.error("confirmCV not available in this protocol");
239        throw new ProgrammerException();
240    }
241
242    /** 
243     * {@inheritDoc}
244     *
245     * Can this ops-mode programmer read back values? For now, no, but maybe
246     * later.
247     *
248     * @return always false for now
249     */
250    @Override
251    public boolean getCanRead() {
252        return false;
253    }
254
255    /** 
256     * {@inheritDoc}
257     */
258    @Override
259    public boolean getLongAddress() {
260        return mLongAddr;
261    }
262
263    /** 
264     * {@inheritDoc}
265     */
266    @Override
267    public int getAddressNumber() {
268        return mAddress;
269    }
270
271    /** 
272     * {@inheritDoc}
273     */
274    @Override
275    public String getAddress() {
276        return "" + getAddressNumber() + " " + getLongAddress();
277    }
278
279    // initialize logging
280    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TmccOpsModeProgrammer.class);
281
282
283}