001package jmri.jmrix.tmcc;
002
003import java.util.ArrayList;
004import java.util.List;
005import javax.annotation.Nonnull;
006
007import jmri.ProgrammingMode;
008import jmri.jmrix.AbstractProgrammer;
009
010
011/**
012 * Implements the jmri.Programmer interface via commands for the TMCC
013 * control interface.
014 *
015 * Made from the EasyDCC programmer
016 *
017 * @author Bob Jacobsen Copyright (C) 2001, 2025
018 * with edits/additions by
019 * @author Timothy Jump Copyright (C) 2025
020 */
021public class TmccProgrammer extends AbstractProgrammer {
022
023    public TmccProgrammer(TmccSystemConnectionMemo memo) {
024        tc = memo.getTrafficController();
025    }
026
027    protected SerialTrafficController tc = null;
028
029    /** 
030     * {@inheritDoc}
031     */
032    @Override
033    @Nonnull
034    public List<ProgrammingMode> getSupportedModes() {
035        List<ProgrammingMode> ret = new ArrayList<ProgrammingMode>();
036
037        ret.add(TmccProgrammerManager.TMCCMODE1_ENGID);
038        ret.add(TmccProgrammerManager.TMCCMODE2_ENGID);
039        
040        ret.add(TmccProgrammerManager.TMCCMODE1_TRKID);
041        ret.add(TmccProgrammerManager.TMCCMODE2_TRKID);
042
043        ret.add(TmccProgrammerManager.TMCCMODE1_SWID);
044        ret.add(TmccProgrammerManager.TMCCMODE1_ACCID);
045
046        return ret;
047
048    }
049
050
051    int _cv; // points to "CV" input from Simple Programmer
052    int _val; // points to "Value" input from Simple Programmer
053    int _func; // points to "SET" command for TMCC1 and TMCC2 loco and track ID numbers
054
055
056    /** 
057     * {@inheritDoc}
058     */
059    @Override
060    public synchronized void writeCV(String CVname, int val, jmri.ProgListener p) throws jmri.ProgrammerException {
061        final int CV = Integer.parseInt(CVname);
062
063        _cv = CV;  // Value from Simple Programmer "CV" input
064        _val = val; // Value from Simple Programmer "Value" input
065        _func = 0x00002B; // SET command for both TMCC1 and TMCC2
066
067        // validate CV == 1 for TMCC loco ID programming
068        // validate ID#/address for TMCC is between 1-98
069        // format and send the TMCC loco ID write message
070        // note: the argument is long containing 3 bytes
071
072        if (CV == 1) {
073            
074            if (val > 0 && val < 99) {
075
076                if (getMode() == TmccProgrammerManager.TMCCMODE1_ENGID) {
077                    SerialMessage m = new SerialMessage();
078                    m.setOpCode(0xFE); // set the first byte/TMCC1 opcode to 0xFE
079                    m.putAsWord((val * 128) + _func); // set the second/third byte (address/SET command for TMCC1)
080                    tc.sendSerialMessage(m, null);
081                }
082
083                if  (getMode() == TmccProgrammerManager.TMCCMODE2_ENGID) {
084                    SerialMessage m = new SerialMessage();
085                    m.setOpCode(0xF8); // set the first byte/TMCC2 opcode to 0xF8 for ENGID
086                    m.putAsWord(((val * 512) + 256) + _func); // set the second/third byte (address/SET command for TMCC2)
087                    tc.sendSerialMessage(m, null);
088                }
089                
090                if (getMode() == TmccProgrammerManager.TMCCMODE1_TRKID) {
091                    if (val < 10) {
092                        SerialMessage m = new SerialMessage();
093                        m.setOpCode(0xFE); // set the first byte/TMCC1 opcode to 0xFE
094                        m.putAsWord(((val * 128) + 51200)+ _func); // set the second/third byte (address/SET command for TMCC1)
095                        tc.sendSerialMessage(m, null);
096
097                    } else {
098                        SerialMessage m = new SerialMessage();
099                        m.setOpCode(0x00);
100                        m.putAsWord(00000);
101                        tc.sendSerialMessage(m, null);
102                        log.warn("Address Must be Between 1-9 for TMCC1_TRK");
103                    }
104                }
105
106                if  (getMode() == TmccProgrammerManager.TMCCMODE2_TRKID) {
107                    SerialMessage m = new SerialMessage();
108                    m.setOpCode(0xF9); // set the first byte/TMCC2 opcode to 0xF9 for TRKID
109                    m.putAsWord(((val * 512) + 256) + _func); // set the second/third byte (address/SET command for TMCC2)
110                    tc.sendSerialMessage(m, null);
111                }
112
113                if  (getMode() == TmccProgrammerManager.TMCCMODE1_SWID) {
114                    SerialMessage m = new SerialMessage();
115                    m.setOpCode(0xFE); // set the first byte/TMCC1 opcode to 0xFE
116                    m.putAsWord(((val * 128) + 16384) + _func); // set the second/third byte (address/SET command for TMCC2)
117                    tc.sendSerialMessage(m, null);
118                }
119                
120                if  (getMode() == TmccProgrammerManager.TMCCMODE1_ACCID) {
121                    SerialMessage m = new SerialMessage();
122                    m.setOpCode(0xFE); // set the first byte/TMCC1 opcode to 0xFE
123                    m.putAsWord(((val * 128) + 32768) + _func); // set the second/third byte (address/SET command for TMCC2)
124                    tc.sendSerialMessage(m, null);
125                }              
126
127            } else {
128                SerialMessage m = new SerialMessage();
129                m.setOpCode(0x00);
130                m.putAsWord(00000);
131                tc.sendSerialMessage(m, null);
132                log.warn("Address Must be Between 1-98 for TMCC");
133            }
134
135        } else {
136            SerialMessage m = new SerialMessage();
137            m.setOpCode(0x00);
138            m.putAsWord(00001);
139            tc.sendSerialMessage(m, null);
140            log.warn("CV Must Equal 1 for Programming TMCC Loco/Engine, Switch, Accessory ID#s");
141        }
142
143        // End the "writing..." process in SimpleProgrammer
144        notifyProgListenerEnd(p, _val, jmri.ProgListener.OK);
145 
146    }
147
148
149    /** 
150     * {@inheritDoc}
151     */
152    @Override
153    public synchronized void confirmCV(String CV, int val, jmri.ProgListener p) throws jmri.ProgrammerException {
154    }
155
156    /** 
157     * {@inheritDoc}
158     */
159    @Override
160    public synchronized void readCV(String CVname, jmri.ProgListener p) throws jmri.ProgrammerException {
161    }
162
163
164    /** 
165     * {@inheritDoc}
166     */
167    @Override
168    synchronized protected void timeout() {
169    }
170    
171    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TmccProgrammer.class);
172
173}