001package jmri.jmrix.can.cbus;
002
003import java.util.ArrayList;
004import java.util.List;
005import javax.annotation.Nonnull;
006
007import jmri.AddressedProgrammer;
008import jmri.ProgrammingMode;
009import jmri.jmrix.AbstractProgrammer;
010import jmri.jmrix.can.CanListener;
011import jmri.jmrix.can.CanMessage;
012import jmri.jmrix.can.TrafficController;
013
014/**
015 * Implements the jmri.Programmer interface via commands for CBUS.
016 *
017 * @author Bob Jacobsen Copyright (C) 2008
018 * @deprecated since 4.17.1; use {@link jmri.jmrix.can.cbus.node.CbusNode} instead
019 */
020@Deprecated
021public class CbusProgrammer extends AbstractProgrammer implements CanListener, AddressedProgrammer {
022
023    public CbusProgrammer(int nodenumber, TrafficController tc) {
024        this.nodenumber = nodenumber;
025        // need a longer LONG_TIMEOUT
026        LONG_TIMEOUT = 180000;
027        this.tc = tc;
028        setMode(CBUSNODEVARMODE);
029    }
030
031    TrafficController tc;
032
033    int nodenumber;
034
035    /** 
036     * {@inheritDoc}
037     *
038     * Types implemented here.
039     */
040    @Override
041    @Nonnull
042    public List<ProgrammingMode> getSupportedModes() {
043        List<ProgrammingMode> ret = new ArrayList<ProgrammingMode>();
044        ret.add(CBUSNODEVARMODE);
045        return ret;
046    }
047
048    final static ProgrammingMode CBUSNODEVARMODE = new ProgrammingMode("CBUSNODEVARMODE", "CBUSNODEVARMODE");
049
050    // members for handling the programmer interface
051    int progState = 0;
052    static final int NOTPROGRAMMING = 0;// is notProgramming
053    static final int COMMANDSENT = 2;  // read/write command sent, waiting reply
054    boolean programmerReadOperation = false;  // true reading, false if writing
055    int operationValue;  // remember the value being read/written for confirmative reply
056    int operationVariableNumber; // remember the variable number being read/written
057
058    /** 
059     * {@inheritDoc}
060     */
061    @Override
062    synchronized public void writeCV(String CVname, int val, jmri.ProgListener p) throws jmri.ProgrammerException {
063        final int varnum = Integer.parseInt(CVname);
064        if (log.isDebugEnabled()) {
065            log.debug("write {} listens {}", varnum, p);
066        }
067        useProgrammer(p);
068        programmerReadOperation = false;
069        // set state
070        progState = NOTPROGRAMMING;  // no reply to write
071        operationValue = val;
072        operationVariableNumber = varnum;
073
074        // format and send the write message.
075        int[] frame = new int[]{0x96, (nodenumber / 256) & 0xFF, nodenumber & 0xFF, operationVariableNumber & 0xFF, operationValue & 0xFF};
076        CanMessage m = new CanMessage(frame, tc.getCanid());
077        tc.sendCanMessage(m, this);
078
079        // no reply, so don't want for reply
080        progState = NOTPROGRAMMING;
081        notifyProgListenerEnd(operationValue, jmri.ProgListener.OK);
082    }
083
084    /** 
085     * {@inheritDoc}
086     */
087    @Override
088    synchronized public void confirmCV(String varnum, int val, jmri.ProgListener p) throws jmri.ProgrammerException {
089        readCV(varnum, p);
090    }
091
092    /** 
093     * {@inheritDoc}
094     */
095    @Override
096    synchronized public void readCV(String CVname, jmri.ProgListener p) throws jmri.ProgrammerException {
097        final int varnum = Integer.parseInt(CVname);
098        if (log.isDebugEnabled()) {
099            log.debug("readCV {} listens {}", varnum, p);
100        }
101        useProgrammer(p);
102        programmerReadOperation = true;
103
104        progState = COMMANDSENT;
105        operationVariableNumber = varnum;
106
107        // start the error timer
108        startLongTimer();
109
110        // format and send the read message.
111        int[] frame = new int[]{0x71, (nodenumber / 256) & 0xFF, nodenumber & 0xFF, operationVariableNumber & 0xFF};
112        CanMessage m = new CanMessage(frame, tc.getCanid());
113        tc.sendCanMessage(m, this);
114    }
115
116    private jmri.ProgListener programmerUser = null;  // null if don't have one
117
118    // internal method to remember who's using the programmer
119    protected void useProgrammer(jmri.ProgListener p) throws jmri.ProgrammerException {
120        // test for only one!
121        if (programmerUser != null && programmerUser != p) {
122            if (log.isDebugEnabled()) {
123                log.debug("programmer already in use by {}", programmerUser);
124            }
125            throw new jmri.ProgrammerException("programmer in use");
126        } else {
127            programmerUser = p;
128            return;
129        }
130    }
131
132    /** 
133     * {@inheritDoc}
134     */
135    @Override
136    public void message(CanMessage m) {
137        log.debug("message received and ignored: {}", m.toString());
138    }
139
140    /** 
141     * {@inheritDoc}
142     */
143    @Override
144    synchronized public void reply(jmri.jmrix.can.CanReply m) {
145        if (progState == NOTPROGRAMMING) {
146            // we get the complete set of replies now, so ignore these
147            if (log.isDebugEnabled()) {
148                log.debug("reply in NOTPROGRAMMING state");
149            }
150            return;
151        } else if (progState == COMMANDSENT) {
152            if (log.isDebugEnabled()) {
153                log.debug("reply in COMMANDSENT state");
154            }
155            // operation done, capture result, then have to leave programming mode
156            progState = NOTPROGRAMMING;
157            // check for reply
158            if (m.getElement(0) == 0x97
159                    && (m.getElement(1) == ((nodenumber / 256) & 0xFF))
160                    && (m.getElement(2) == (nodenumber & 0xFF))) {
161                // this is the OK reply
162                // see why waiting
163                if (programmerReadOperation) {
164                    // read was in progress - get return value
165                    operationValue = m.getElement(3) & 0xFF;
166                }
167                // if this was a read, we retrieved the value above.  If its a
168                // write, we're to return the original write value
169                notifyProgListenerEnd(operationValue, jmri.ProgListener.OK);
170            }
171        }
172    }
173
174    /** 
175     * {@inheritDoc}
176     *
177     * Internal routine to handle a timeout
178     */
179    @Override
180    synchronized protected void timeout() {
181        if (progState != NOTPROGRAMMING) {
182            // we're programming, time to stop
183            if (log.isDebugEnabled()) {
184                log.debug("timeout!");
185            }
186            // perhaps no loco present? Fail back to end of programming
187            progState = NOTPROGRAMMING;
188            cleanup();
189            notifyProgListenerEnd(operationValue, jmri.ProgListener.FailedTimeout);
190        }
191    }
192
193    /** 
194     * {@inheritDoc}
195     */
196    @Override
197    public boolean getLongAddress() {
198        return false;
199    }
200
201    /** 
202     * {@inheritDoc}
203     */
204    @Override
205    public int getAddressNumber() {
206        return nodenumber;
207    }
208
209    /** 
210     * {@inheritDoc}
211     */
212    @Override
213    public String getAddress() {
214        return "" + getAddressNumber() + " " + getLongAddress();
215    }
216
217    /**
218     * Internal method to send a cleanup message (if needed) on timeout.
219     * <p>
220     * Here, it sends a request to exit from programming mode. But subclasses,
221     * e.g. ops mode, may redefine that.
222     */
223    void cleanup() {
224        // tc.sendEasyDccMessage(EasyDccMessage.getExitProgMode(), this);
225    }
226
227    // internal method to notify of the final result
228    protected void notifyProgListenerEnd(int value, int status) {
229        if (log.isDebugEnabled()) {
230            log.debug("notifyProgListenerEnd value {} status {}", value, status);
231        }
232        // the programmingOpReply handler might send an immediate reply, so
233        // clear the current listener _first_
234        jmri.ProgListener temp = programmerUser;
235        programmerUser = null;
236        notifyProgListenerEnd(temp,value,status);
237    }
238
239    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CbusProgrammer.class);
240}