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