001package jmri.jmrix.nce;
002
003import java.util.ArrayList;
004import java.util.List;
005import javax.annotation.Nonnull;
006
007import jmri.AddressedProgrammer;
008import jmri.NmraPacket;
009import jmri.ProgListener;
010import jmri.ProgrammerException;
011import jmri.ProgrammingMode;
012
013/**
014 * Provide an Ops Mode Programmer via a wrapper what works with the NCE command
015 * station object.
016 * <p>
017 * Functionally, this just creates packets to send via the command station.
018 *
019 * @see jmri.Programmer
020 * @author Bob Jacobsen Copyright (C) 2002, 2014
021 * @author kcameron Copyright (C) 2014
022 */
023public class NceOpsModeProgrammer extends NceProgrammer implements AddressedProgrammer {
024
025    int mAddress;
026    boolean mLongAddr;
027
028    public NceOpsModeProgrammer(NceTrafficController tc, int pAddress, boolean pLongAddr) {
029        super(tc);
030        log.debug("NCE ops mode programmer {} {}", pAddress, pLongAddr);
031        mAddress = pAddress;
032        mLongAddr = pLongAddr;
033        setMode(ProgrammingMode.OPSBYTEMODE);
034    }
035
036    /**
037     * {@inheritDoc}
038     *
039     * Forward a write request to an ops-mode write operation
040     */
041    @Override
042    public synchronized void writeCV(String CVname, int val, ProgListener p) throws ProgrammerException {
043        final int CV = Integer.parseInt(CVname);
044        if (log.isDebugEnabled()) {
045            log.debug("write CV={} val={}", CV, val);
046        }
047        NceMessage msg;
048        // USB can't send a NMRA packet, must use new ops mode command
049        if (tc.getUsbSystem() == NceTrafficController.USB_SYSTEM_POWERCAB
050                || tc.getUsbSystem() == NceTrafficController.USB_SYSTEM_SB3
051                || tc.getUsbSystem() == NceTrafficController.USB_SYSTEM_SB5
052                || tc.getUsbSystem() == NceTrafficController.USB_SYSTEM_TWIN) {
053            int locoAddr = mAddress;
054            if (mLongAddr) {
055                locoAddr += 0xC000;
056            }
057            byte[] bl = NceBinaryCommand.usbOpsModeLoco(tc, locoAddr, CV, val);
058            msg = NceMessage.createBinaryMessage(tc, bl);
059
060        } else {
061            // create the message and fill it,
062            byte[] contents = NmraPacket.opsCvWriteByte(mAddress, mLongAddr,
063                    CV, val);
064            if (contents == null) {
065                throw new ProgrammerException();
066            }
067            msg = NceMessage.sendPacketMessage(tc, contents, 5); // retry 5 times
068        }
069        // record state. COMMANDSENT is just waiting for a reply...
070        useProgrammer(p);
071        _progRead = false;
072        progState = COMMANDSENT_2;
073        _val = val;
074        _cv = CV;
075
076        // start the error timer
077        startShortTimer();
078
079        // send it twice (2x5) so NCE CS will send at least two consecutive commands to decoder
080        tc.sendNceMessage(msg, this);
081        tc.sendNceMessage(msg, this);
082    }
083
084    /**
085     * {@inheritDoc}
086     */
087    @Override
088    public synchronized void readCV(String CVname, ProgListener p) throws ProgrammerException {
089        final int CV = Integer.parseInt(CVname);
090        if (log.isDebugEnabled()) {
091            log.debug("read CV={}", CV);
092        }
093        log.error("readCV not available in this protocol");
094        throw new ProgrammerException();
095    }
096
097    /**
098     * {@inheritDoc}
099     */
100    @Override
101    public synchronized void confirmCV(String CV, int val, ProgListener p) throws ProgrammerException {
102        if (log.isDebugEnabled()) {
103            log.debug("confirm CV={}", CV);
104        }
105        log.error("confirmCV not available in this protocol");
106        throw new ProgrammerException();
107    }
108
109    /**
110     * {@inheritDoc}
111     *
112     *  add 200mSec between commands, so NCE command station queue doesn't get overrun
113     */
114    @Override
115    protected void notifyProgListenerEnd(int value, int status) {
116        if (log.isDebugEnabled()) {
117            log.debug("NceOpsModeProgrammer adds 200mSec delay to response");
118        }
119        try {
120            wait(200);
121        } catch (InterruptedException e) {
122            log.debug("unexpected exception", e);
123        }
124        super.notifyProgListenerEnd(value, status);
125    }
126
127    /**
128     * {@inheritDoc}
129     *
130     * Types implemented here.
131     */
132    @Override
133    @Nonnull
134    public List<ProgrammingMode> getSupportedModes() {
135        List<ProgrammingMode> ret = new ArrayList<ProgrammingMode>();
136        ret.add(ProgrammingMode.OPSBYTEMODE);
137        return ret;
138    }
139
140    /**
141     * {@inheritDoc}
142     *
143     * Can this ops-mode programmer read back values? For now, no, but maybe
144     * later.
145     *
146     * @return always false for now
147     */
148    @Override
149    public boolean getCanRead() {
150        return false;
151    }
152
153    /**
154     * {@inheritDoc}
155     *
156     * Ops-mode programming doesn't put the command station in programming mode,
157     * so we don't have to send an exit-programming command at end. Therefore,
158     * this routine does nothing except to replace the parent routine that does
159     * something.
160     */
161    @Override
162    void cleanup() {
163    }
164
165    /**
166     * {@inheritDoc}
167     */
168    @Override
169    public boolean getLongAddress() {
170        return mLongAddr;
171    }
172
173    /**
174     * {@inheritDoc}
175     */
176    @Override
177    public int getAddressNumber() {
178        return mAddress;
179    }
180
181    /**
182     * {@inheritDoc}
183     */
184    @Override
185    public String getAddress() {
186        return "" + getAddressNumber() + " " + getLongAddress();
187    }
188
189    // initialize logging
190    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(NceOpsModeProgrammer.class);
191
192}