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