001package jmri;
002
003import java.beans.PropertyChangeListener;
004import java.util.List;
005import javax.annotation.Nonnull;
006
007/**
008 * Provide access to the hardware DCC decoder programming capability.
009 * <p>
010 * Programmers come in multiple types:
011 * <ul>
012 * <li>Global, previously "Service Mode" or on a programming track
013 * <li>Addressed, previously "Ops Mode" also known as "programming on the main"
014 * </ul>
015 * Different equipment may also require different programmers:
016 * <ul>
017 * <li>DCC CV programming, on service mode track or on the main
018 * <li>CBUS Node Variable programmers
019 * <li>LocoNet System Variable programmers
020 * <li>LocoNet Op Switch programmers
021 * <li>etc
022 * </ul>
023 * Depending on which type you have, only certain modes can be set. Valid modes
024 * are specified by the class static constants.
025 * <p>
026 * You get a Programmer object from a {@link GlobalProgrammerManager} or an
027 * {@link AddressedProgrammerManager}, which in turn can be located from the
028 * {@link InstanceManager}.
029 * <p>
030 * Starting in JMRI 3.5.5, the CV addresses are Strings for generality. The
031 * methods that use ints for CV addresses will later be deprecated.
032 * <p>
033 * Added possibility to supply CV value hint to the system
034 * <hr>
035 * This file is part of JMRI.
036 * <p>
037 * JMRI is free software; you can redistribute it and/or modify it under the
038 * terms of version 2 of the GNU General Public License as published by the Free
039 * Software Foundation. See the "COPYING" file for a copy of this license.
040 * <p>
041 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY
042 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
043 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
044 *
045 * @see jmri.GlobalProgrammerManager
046 * @see jmri.AddressedProgrammerManager
047 * @author Bob Jacobsen Copyright (C) 2001, 2008, 2013
048 * @author Andrew Crosland (C) 2021
049 */
050public interface Programmer extends jmri.Disposable {
051
052    /**
053     * Perform a CV write in the system-specific manner, and using the specified
054     * programming mode.
055     * <p>
056     * Handles a general address space through a String address. Each programmer
057     * defines the acceptable formats.
058     * <p>
059     * Note that this returns before the write is complete; you have to provide
060     * a ProgListener to hear about completion. For simplicity, expect the return to be on the 
061     * <a href="http://jmri.org/help/en/html/doc/Technical/Threads.shtml">GUI thread</a>.
062     * <p>
063     * Exceptions will only be
064     * thrown at the start, not during the actual programming sequence. A
065     * typical exception would be due to an invalid mode (though that should be
066     * prevented earlier)
067     *
068     * @param CV  the CV to write
069     * @param val the value to write
070     * @param p   the listener that will be notified of the write
071     * @throws jmri.ProgrammerException if unable to communicate
072     */
073    void writeCV(String CV, int val, ProgListener p) throws ProgrammerException;
074
075    /**
076     * Perform a CV read in the system-specific manner, and using the specified
077     * programming mode.
078     * <p>
079     * Handles a general address space through a String address. Each programmer
080     * defines the acceptable formats.
081     * <p>
082     * Note that this returns before the write is complete; you have to provide
083     * a ProgListener to hear about completion. For simplicity, expect the return to be on the 
084     * <a href="http://jmri.org/help/en/html/doc/Technical/Threads.shtml">GUI thread</a>.
085     * <p>
086     * Exceptions will only be
087     * thrown at the start, not during the actual programming sequence. A
088     * typical exception would be due to an invalid mode (though that should be
089     * prevented earlier)
090     *
091     * @param CV the CV to read
092     * @param p  the listener that will be notified of the read
093     * @throws jmri.ProgrammerException if unable to communicate
094     */
095    void readCV(String CV, ProgListener p) throws ProgrammerException;
096
097    /**
098     * Perform a CV read in the system-specific manner, and using the specified
099     * programming mode, possibly using a hint of the current value to speed up
100     * programming.
101     * <p>
102     * Handles a general address space through a String address. Each programmer
103     * defines the acceptable formats.
104     * <p>
105     * On systems that support it, the startVal is a hint as to what the current
106     * value of the CV might be (e.g. the value from the roster). This could be
107     * verified immediately in direct byte mode to speed up the read process.
108     * <p>
109     * Note that this returns before the write is complete; you have to provide
110     * a ProgListener to hear about completion. For simplicity, expect the return to be on the 
111     * <a href="http://jmri.org/help/en/html/doc/Technical/Threads.shtml">GUI thread</a>.
112     * <p>
113     * Defaults to the normal read method if not overridden in a specific implementation.
114     * <p>
115     * Exceptions will only be
116     * thrown at the start, not during the actual programming sequence. A
117     * typical exception would be due to an invalid mode (though that should be
118     * prevented earlier)
119     *
120     * @param CV the CV to read
121     * @param p  the listener that will be notified of the read
122     * @param startVal  a hint of what the current value might be, or 0
123     * @throws jmri.ProgrammerException if unable to communicate
124     */
125    default void readCV(String CV, ProgListener p, int startVal) throws ProgrammerException {
126        readCV(CV, p);
127    }
128
129    /**
130     * Confirm the value of a CV using the specified programming mode. On some
131     * systems, this is faster than a read.
132     * <p>
133     * Handles a general address space through a String address. Each programmer
134     * defines the acceptable formats.
135     * <p>
136     * Note that this returns before the write is complete; you have to provide
137     * a ProgListener to hear about completion. For simplicity, expect the return to be on the 
138     * <a href="http://jmri.org/help/en/html/doc/Technical/Threads.shtml">GUI thread</a>.
139     * <p>
140     * Exceptions will only be
141     * thrown at the start, not during the actual programming sequence. A
142     * typical exception would be due to an invalid mode (though that should be
143     * prevented earlier)
144     *
145     * @param CV  the CV to confirm
146     * @param val the value to confirm
147     * @param p   the listener that will be notified of the confirmation
148     * @throws jmri.ProgrammerException if unable to communicate
149     */
150    void confirmCV(String CV, int val, ProgListener p) throws ProgrammerException;
151
152    /**
153     * Get the list of {@link ProgrammingMode} supported by this Programmer. If
154     * the order is significant, earlier modes are better.
155     *
156     * @return the list of supported modes or an empty list
157     */
158    @Nonnull
159    List<ProgrammingMode> getSupportedModes();
160
161    /**
162     * Set the programmer to a particular mode.
163     * <p>
164     * Mode is a bound parameter; mode changes fire listeners.
165     * <p>
166     * Only modes returned by {@link #getSupportedModes} are supported. If an
167     * invalid mode is requested, the active mode is unchanged.
168     *
169     * @param p a valid node returned by {@link #getSupportedModes()} or null;
170     *          null is ignored if {@link #getSupportedModes()} is not empty
171     */
172    void setMode(ProgrammingMode p);
173
174    /**
175     * Get the current programming mode
176     *
177     * @return the current mode or null if none is defined and no default mode
178     *         is defined
179     */
180    ProgrammingMode getMode();
181
182    /**
183     * Checks the general read capability, regardless of mode
184     *
185     * @return true if the programmer is capable of reading; false otherwise
186     */
187    boolean getCanRead();
188
189    /**
190     * Checks the general read capability, regardless of mode, for a specific
191     * address
192     *
193     * @param addr the address to read
194     * @return true if the address can be read; false otherwise
195     */
196    boolean getCanRead(String addr);
197
198    /**
199     * Checks the general write capability, regardless of mode
200     *
201     * @return true if the programmer is capable of writing; false otherwise
202     */
203    boolean getCanWrite();
204
205    /**
206     * Checks the general write capability, regardless of mode, for a specific
207     * address
208     *
209     * @param addr the address to write to
210     * @return true if the address can be written to; false otherwise
211     */
212    boolean getCanWrite(String addr);
213
214    /**
215     * Learn about whether the programmer does any kind of verification of write
216     * operations
217     *
218     * @param addr A CV address to check (in case this varies with CV range) or
219     *             null for any
220     * @return The confirmation behavior that can be counted on (might be better
221     *         in some cases)
222     */
223    @Nonnull
224    WriteConfirmMode getWriteConfirmMode(String addr);
225
226    enum WriteConfirmMode {
227        /**
228         * No verification available, writes are blind
229         */
230        NotVerified,
231        /**
232         * Programmer signals error if there's no reply from the device
233         */
234        DecoderReply,
235        /**
236         * Programmer does a read after write to verify
237         */
238        ReadAfterWrite
239    }
240
241    /**
242     * wrapper to call {@link jmri.ProgListener#programmingOpReply} that verifies
243     * the specified progListener is not null.
244     *
245     * @param p listener to notify
246     * @param value result value
247     * @param status code from jmri.ProgListener 
248     */
249    default void notifyProgListenerEnd(ProgListener p, int value, int status) {
250        if ( p != null ) {
251           p.programmingOpReply(value, status);
252        }
253    }
254
255    void addPropertyChangeListener(PropertyChangeListener p);
256
257    void removePropertyChangeListener(PropertyChangeListener p);
258
259    // error handling on request is via exceptions
260    // results are returned via the ProgListener callback
261    @Nonnull
262    String decodeErrorCode(int i);
263
264    /**
265     * Free up system resources.
266     * Overriding classes should be capable of this being called
267     * multiple times as per the {@link jmri.Disposable} interface.
268     * {@inheritDoc}
269     */
270    @Override
271    default void dispose() {}
272
273}