001package jmri.jmrix.dccpp;
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;
011import org.slf4j.Logger;
012import org.slf4j.LoggerFactory;
013
014/**
015 * Provides an Ops mode programming interface for DCC++. Currently only Byte
016 * mode is implemented, though DCC++ also supports bit mode writes for POM
017 *
018 * @see jmri.Programmer
019 * @author Paul Bender Copyright (C) 2003-2010
020 * @author Girgio Terdina Copyright (C) 2007
021 * @author Mark Underwood Copyright (C) 2015
022 *
023 * Based on XNetOpsModeProgrammer by Paul Bender and Girgio Terdina
024 */
025public class DCCppOpsModeProgrammer extends jmri.jmrix.AbstractProgrammer implements AddressedProgrammer {
026
027    int mAddressHigh;
028    int mAddressLow;
029    int mAddress;
030    int progState = 0;
031    int value;
032    ProgListener progListener = null;
033
034    protected DCCppTrafficController tc;
035
036    public DCCppOpsModeProgrammer(int pAddress, DCCppTrafficController controller) {
037        tc = controller;
038        if (log.isDebugEnabled()) {
039            log.debug("Creating Ops Mode Programmer for Address {}", pAddress);
040        }
041        mAddressLow = DCCppCommandStation.getDCCAddressLow(pAddress);
042        mAddressHigh = DCCppCommandStation.getDCCAddressHigh(pAddress);
043        mAddress = pAddress;
044        if (log.isDebugEnabled()) {
045            log.debug("High Address: {} Low Address: {}", mAddressHigh, mAddressLow);
046        }
047    }
048
049    /** 
050     * {@inheritDoc}
051     *
052     * Send an ops-mode write request to the DCC++.
053     */
054    @Override
055    synchronized public void writeCV(String CVname, int val, ProgListener p) throws ProgrammerException {
056        final int CV = Integer.parseInt(CVname);
057        DCCppMessage msg = DCCppMessage.makeWriteOpsModeCVMsg(mAddress, CV, val);
058        tc.sendDCCppMessage(msg, null);
059        /* we need to save the programmer and value so we can send messages
060         back to the programming screen when we receive something from the
061         command station */
062        progListener = p;
063        value = val;
064        progState = DCCppProgrammer.REQUESTSENT;
065        assert msg != null;
066        restartTimer(msg.getTimeout());
067        /* Issue #2423 (GitHub) -- DCC++ base station does not respond to Ops Mode
068         * writes, so waiting for a response just means JMRI times out after a long delay.
069        /* Proposed Fix: Don't go to REQUESTSENT state... just stay in NOTPROGRAMMING.
070         * Risk... the state change introduces a 250ms delay to keep the UI from sending
071         * write commands too frequently... so we'd have to do that here too.
072        */
073        // Before we set the programmer state to not programming, 
074        // delay for a short time to give the decoder a chance to 
075        // process the request.
076        try {
077            this.wait(250); // Spotbugs not happy: WAIT_NOT_IN_LOOP
078        } catch (java.lang.InterruptedException ie) {
079            log.debug("Interrupted During Delay");
080        }
081        progState = DCCppProgrammer.NOTPROGRAMMING;
082        stopTimer();
083        notifyProgListenerEnd(progListener, value, ProgListener.OK);
084    }
085
086    /** 
087     * {@inheritDoc}
088     */
089    @Override
090    synchronized public void readCV(String CVname, ProgListener p) throws ProgrammerException {
091        notifyProgListenerEnd(p, Integer.parseInt(CVname), ProgListener.NotImplemented);
092    }
093
094    /** 
095     * {@inheritDoc}
096     */
097    @Override
098    public void confirmCV(String CV, int val, ProgListener p) throws ProgrammerException {
099        notifyProgListenerEnd(p, val, ProgListener.NotImplemented);
100    }
101
102    /** 
103     * {@inheritDoc}
104     *
105     * Types implemented here.
106     */
107    @Override
108    @Nonnull
109    public List<ProgrammingMode> getSupportedModes() {
110        List<ProgrammingMode> ret = new ArrayList<>();
111        ret.add(ProgrammingMode.OPSBYTEMODE);
112        ret.add(ProgrammingMode.OPSBITMODE);
113        return ret;
114    }
115
116    /** 
117     * {@inheritDoc}
118     */
119    @Override
120    public boolean getLongAddress() {
121        return true;
122    }
123
124    /** 
125     * {@inheritDoc}
126     */
127    @Override
128    public int getAddressNumber() {
129        return mAddress;
130    }
131
132    /** 
133     * {@inheritDoc}
134     */
135    @Override
136    public String getAddress() {
137        return "" + getAddressNumber() + " " + getLongAddress();
138    }
139
140    /** 
141     * {@inheritDoc}
142     */
143    @Override
144    synchronized protected void timeout() {
145        progState = DCCppProgrammer.NOTPROGRAMMING;
146        stopTimer();
147        notifyProgListenerEnd(progListener, value, ProgListener.FailedTimeout);
148    }
149
150    // initialize logging
151    private final static Logger log = LoggerFactory.getLogger(DCCppOpsModeProgrammer.class);
152
153}