001package jmri.jmrix.nce;
002
003import java.util.Arrays;
004
005import javax.annotation.CheckForNull;
006import javax.annotation.Nonnull;
007
008import jmri.jmrix.nce.ncemon.NceMonBinary;
009
010/**
011 * Encodes a message to an NCE command station.
012 * <p>
013 * The {@link NceReply} class handles the response from the command station.
014 * <p>
015 * The NCE protocol has "binary" and "ASCII" command sets. Depending on the
016 * version of the EPROM it contains, NCE command stations have different support
017 * for command sets:
018 * <ul>
019 * <li>1999 - All ASCII works. Binary works except for programming.
020 * <li>2004 - ASCII needed for programming, binary for everything else.
021 * <li>2006 - binary needed for everything
022 * </ul>
023 * See the {@link NceTrafficController#setCommandOptions(int)} method for more
024 * information.
025 * <p>
026 * Apparently the binary "exitProgrammingMode" command can crash the command
027 * station if the EPROM was built before 2006. This method uses a state flag
028 * ({@link NceTrafficController#getNceProgMode}) to detect whether a command to
029 * enter program mode has been generated, and presumably sent, when using the
030 * later EPROMS.
031 *
032 * @author Bob Jacobsen Copyright (C) 2001
033 * @author Daniel Boudreau Copyright (C) 2007
034 * @author kcameron Copyright (C) 2014
035 */
036public class NceMessage extends jmri.jmrix.AbstractMRMessage {
037 
038    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(NceMessage.class); // called in static block
039
040    public static final int NOP_CMD = 0x80; //NCE NOP command
041    public static final int ASSIGN_CAB_CMD = 0x81; // NCE Assign loco to cab command, NCE-USB no
042    public static final int READ_CLOCK_CMD = 0x82; // NCE read clock command, NCE-USB no
043    public static final int STOP_CLOCK_CMD = 0x83; // NCE stop clock command, NCE-USB no
044    public static final int START_CLOCK_CMD = 0x84; // NCE start clock command, NCE-USB no
045    public static final int SET_CLOCK_CMD = 0x85; // NCE set clock command, NCE-USB no
046    public static final int CLOCK_1224_CMD = 0x86; // NCE change clock 12/24 command, NCE-USB no
047    public static final int CLOCK_RATIO_CMD = 0x87; // NCE set clock ratio command, NCE-USB no
048    public static final int DEQUEUE_CMD = 0x88; // NCE dequeue packets based on loco addr, NCE-USB no
049
050    public static final int READ_AUI4_CMD = 0x8A; // NCE read status of AUI yy, returns four bytes, NCE-USB no
051
052    public static final int DUMMY_CMD = 0x8C; // NCE Dummy instruction, NCE-USB yes
053    public static final int SPEED_MODE_CMD = 0x8D; // NCE set speed mode, NCE-USB no
054    public static final int WRITE_N_CMD = 0x8E; // NCE write up to 16 bytes of memory command, NCE-USB no
055    public static final int READ16_CMD = 0x8F; // NCE read 16 bytes of memory command, NCE-USB no
056    public static final int DISPLAY3_CMD = 0x90; // NCE write 16 char to cab display line 3, NCE-USB no
057    public static final int DISPLAY4_CMD = 0x91; // NCE write 16 char to cab display line 4, NCE-USB no
058    public static final int DISPLAY2_CMD = 0x92; // NCE write 8 char to cab display line 2 right, NCE-USB no
059    public static final int QUEUE3_TMP_CMD = 0x93; // NCE queue 3 bytes to temp queue, NCE-USB no
060    public static final int QUEUE4_TMP_CMD = 0x94; // NCE queue 4 bytes to temp queue, NCE-USB no
061    public static final int QUEUE5_TMP_CMD = 0x95; // NCE queue 5 bytes to temp queue, NCE-USB no
062    public static final int QUEUE6_TMP_CMD = 0x96; // NCE queue 6 bytes to temp queue, NCE-USB no
063    public static final int WRITE1_CMD = 0x97; // NCE write 1 bytes of memory command, NCE-USB no
064    public static final int WRITE2_CMD = 0x98; // NCE write 2 bytes of memory command, NCE-USB no
065    public static final int WRITE4_CMD = 0x99; // NCE write 4 bytes of memory command, NCE-USB no
066    public static final int WRITE8_CMD = 0x9A; // NCE write 8 bytes of memory command, NCE-USB no
067    public static final int READ_AUI2_CMD = 0x9B; // NCE read status of AUI yy, returns two bytes, NCE-USB >= 1.65
068    public static final int MACRO_CMD = 0x9C; // NCE execute macro n, NCE-USB yes
069    public static final int READ1_CMD = 0x9D; // NCE read 1 byte of memory command, NCE-USB no
070    public static final int ENTER_PROG_CMD = 0x9E; //NCE enter programming track mode command
071    public static final int EXIT_PROG_CMD = 0x9F; //NCE exit programming track mode command
072    public static final int WRITE_PAGED_CV_CMD = 0xA0; //NCE write CV paged command
073    public static final int READ_PAGED_CV_CMD = 0xA1; //NCE read CV paged command
074    public static final int LOCO_CMD = 0xA2; // NCE loco control command, NCE-USB yes
075    public static final int QUEUE3_TRK_CMD = 0xA3; // NCE queue 3 bytes to track queue, NCE-USB no
076    public static final int QUEUE4_TRK_CMD = 0xA4; // NCE queue 4 bytes to track queue, NCE-USB no
077    public static final int QUEUE5_TRK_CMD = 0xA5; // NCE queue 5 bytes to track queue, NCE-USB no
078    public static final int WRITE_REG_CMD = 0xA6; //NCE write register command
079    public static final int READ_REG_CMD = 0xA7; //NCE read register command
080    public static final int WRITE_DIR_CV_CMD = 0xA8; //NCE write CV direct command
081    public static final int READ_DIR_CV_CMD = 0xA9; //NCE read CV direct command
082    public static final int SW_REV_CMD = 0xAA; // NCE get EPROM revision cmd, Reply Format: VV.MM.mm, NCE-USB yes
083    public static final int RESET_SOFT_CMD = 0xAB; // NCE soft reset command, NCE-USB no
084    public static final int RESET_HARD_CMD = 0xAC; // NCE hard reset command, NCE-USB no
085    public static final int SEND_ACC_SIG_MACRO_CMD = 0xAD; // NCE send NMRA aspect command
086    public static final int OPS_PROG_LOCO_CMD = 0xAE;   // NCE ops mode program loco, NCE-USB yes
087    public static final int OPS_PROG_ACCY_CMD = 0xAF;   // NCE ops mode program accessories, NCE-USB yes
088    public static final int FACTORY_TEST_CMD = 0xB0;    // NCE factory test, NCE-USB yes
089    public static final int USB_SET_CAB_CMD = 0xB1;     // NCE set cab address in USB, NCE-USB yes
090    public static final int USB_MEM_POINTER_CMD = 0xB3; // NCE set memory context pointer, NCE-USB >= 1.65
091    public static final int USB_MEM_WRITE_CMD = 0xB4;   // NCE write memory, NCE-USB >= 1.65
092    public static final int USB_MEM_READ_CMD = 0xB5;    // NCE read memory, NCE-USB >= 1.65
093
094    // NCE Command 0xA2 sends speed or function packets to a locomotive
095    // 0xA2 sub commands speed and functions
096    public static final byte LOCO_CMD_SELECT_LOCO = 0x00;  // select loco
097    public static final byte LOCO_CMD_REV_28SPEED = 0x01;  // set loco speed 28 steps reverse
098    public static final byte LOCO_CMD_FWD_28SPEED = 0x02;  // set loco speed 28 steps forward
099    public static final byte LOCO_CMD_REV_128SPEED = 0x03; // set loco speed 128 steps reverse
100    public static final byte LOCO_CMD_FWD_128SPEED = 0x04; // set loco speed 128 steps forward
101    public static final byte LOCO_CMD_REV_ESTOP = 0x05;    // emergency stop reverse
102    public static final byte LOCO_CMD_FWD_ESTOP = 0x06;    // emergency stop forward
103    public static final byte LOCO_CMD_FG1 = 0x07;          // function group 1
104    public static final byte LOCO_CMD_FG2 = 0x08;          // function group 2
105    public static final byte LOCO_CMD_FG3 = 0x09;          // function group 3
106    public static final byte LOCO_CMD_FG4 = 0x15;          // function group 4
107    public static final byte LOCO_CMD_FG5 = 0x16;          // function group 5
108
109    // OxA2 sub commands consist
110    public static final byte LOCO_CMD_REV_CONSIST_LEAD = 0x0A;    // reverse consist address for lead loco
111    public static final byte LOCO_CMD_FWD_CONSIST_LEAD = 0x0B;    // forward consist address for lead loco 
112    public static final byte LOCO_CMD_REV_CONSIST_REAR = 0x0C;    // reverse consist address for rear loco 
113    public static final byte LOCO_CMD_FWD_CONSIST_REAR = 0x0D;    // forward consist address for rear loco
114    public static final byte LOCO_CMD_REV_CONSIST_MID = 0x0E;     // reverse consist address for additional loco 
115    public static final byte LOCO_CMD_FWD_CONSIST_MID = 0x0F;     // forward consist address for additional loco 
116    public static final byte LOCO_CMD_DELETE_LOCO_CONSIST = 0x10; // Delete loco from consist
117    public static final byte LOCO_CMD_KILL_CONSIST = 0x11;        // Kill consist
118    
119    // The following commands are not supported by the NCE USB  
120    public static final int ENABLE_MAIN_CMD = 0x89; //NCE enable main track, kill programming command
121    public static final int KILL_MAIN_CMD = 0x8B; //NCE kill main track, enable programming command
122    public static final int SENDn_BYTES_CMD = 0x90; //NCE send 3 to 6 bytes (0x9n, n = 3-6) command
123    public static final int QUEUEn_BYTES_CMD = 0xA0; //NCE queue 3 to 6 bytes (0xAn, n = 3-6) command
124
125    // some constants
126    protected static final int NCE_PAGED_CV_TIMEOUT = 20000;
127    protected static final int NCE_DIRECT_CV_TIMEOUT = 10000;
128    
129    @SuppressWarnings("hiding")  // redefines value from super class
130    protected static final int SHORT_TIMEOUT = 10000; // worst case is when loading the first panel
131
132    public static final int REPLY_1 = 1; // reply length of 1 byte
133    public static final int REPLY_2 = 2; // reply length of 2 bytes
134    public static final int REPLY_3 = 3; // reply length of 3 bytes
135    public static final int REPLY_4 = 4; // reply length of 4 bytes
136    public static final int REPLY_16 = 16; // reply length of 16 bytes 
137    
138    public static char NCE_OKAY = '!';
139
140    public NceMessage() {
141        super();
142    }
143
144    // create a new one
145    public NceMessage(int i) {
146        super(i);
147    }
148
149    // copy one
150    public NceMessage(@Nonnull NceMessage m) {
151        super(m);
152        replyLen = m.replyLen;
153    }
154
155    // from String
156    public NceMessage(@Nonnull String m) {
157        super(m);
158    }
159
160    // default to expecting one reply character
161    int replyLen = 1;
162
163    /**
164     * Set the number of characters expected back from the command station. Used
165     * in binary mode, where there's no end-of-reply string to look for.
166     * 
167     * @param len length of expected reply
168     */
169    public void setReplyLen(int len) {
170        replyLen = len;
171    }
172
173    public int getReplyLen() {
174        return replyLen;
175    }
176
177    // diagnose format
178    public boolean isKillMain() {
179        if (isBinary()) {
180            return getOpCode() == KILL_MAIN_CMD;
181        } else {
182            return getOpCode() == 'K';
183        }
184    }
185
186    public boolean isEnableMain() {
187        if (isBinary()) {
188            return getOpCode() == ENABLE_MAIN_CMD;
189        } else {
190            return getOpCode() == 'E';
191        }
192    }
193
194    // static methods to return a formatted message
195    public static NceMessage getEnableMain(NceTrafficController tc) {
196        // this command isn't supported by the NCE USB
197        if (tc.getUsbSystem() != NceTrafficController.USB_SYSTEM_NONE) {
198            log.error("attempt to send unsupported binary command ENABLE_MAIN_CMD to NCE USB");
199            return null;
200        }
201        NceMessage m = new NceMessage(1);
202        if (tc.getCommandOptions() >= NceTrafficController.OPTION_1999) {
203            m.setBinary(true);
204            m.setReplyLen(1);
205            m.setOpCode(ENABLE_MAIN_CMD);
206        } else {
207            m.setBinary(false);
208            m.setOpCode('E');
209        }
210        return m;
211    }
212
213    public static NceMessage getKillMain(NceTrafficController tc) {
214        // this command isn't supported by the NCE USB
215        if (tc.getUsbSystem() != NceTrafficController.USB_SYSTEM_NONE) {
216            log.error("attempt to send unsupported binary command KILL_MAIN_CMD to NCE USB");
217            return null;
218        }
219        NceMessage m = new NceMessage(1);
220        if (tc.getCommandOptions() >= NceTrafficController.OPTION_1999) {
221            m.setBinary(true);
222            m.setReplyLen(REPLY_1);
223            m.setOpCode(KILL_MAIN_CMD);
224        } else {
225            m.setBinary(false);
226            m.setOpCode('K');
227        }
228        return m;
229    }
230
231    /**
232     * enter programming track mode
233     *
234     * @param tc controller for the associated connection
235     * @return a new message to enter programming track mode
236     */
237    @Nonnull
238    public static NceMessage getProgMode(@Nonnull NceTrafficController tc) {
239        // test if supported on current connection
240        if (tc.getUsbSystem() != NceTrafficController.USB_SYSTEM_NONE &&
241                (tc.getCmdGroups() & NceTrafficController.CMDS_PROGTRACK) != NceTrafficController.CMDS_PROGTRACK) {
242            log.error("attempt to send unsupported binary command ENTER_PROG_CMD to NCE USB");
243            //   return null;
244        }
245        NceMessage m = new NceMessage(1);
246        if (tc.getCommandOptions() >= NceTrafficController.OPTION_2006) {
247            tc.setNceProgMode(true);
248            m.setBinary(true);
249            m.setReplyLen(REPLY_1);
250            m.setOpCode(ENTER_PROG_CMD);
251            m.setTimeout(SHORT_TIMEOUT);
252        } else {
253            m.setBinary(false);
254            m.setOpCode('M');
255            m.setTimeout(SHORT_TIMEOUT);
256        }
257        return m;
258    }
259
260    /**
261     * Apparently the binary "exitProgrammingMode" command can crash the command
262     * station if the EPROM was built before 2006. This method uses a state flag
263     * ({@link NceTrafficController#getNceProgMode}) to detect whether a command
264     * to enter program mode has been generated, and presumably sent, when using
265     * the later EPROMS.
266     *
267     * @param tc controller for the associated connection
268     * @return a new message to exit programming track mode
269     */
270    @CheckForNull
271    public static NceMessage getExitProgMode(@Nonnull NceTrafficController tc) {
272        NceMessage m = new NceMessage(1);
273        if (tc.getCommandOptions() >= NceTrafficController.OPTION_2006) {
274            // Sending exit programming mode binary can crash pre 2006 EPROMs
275            // assumption is that program mode hasn't been entered, so exit without 
276            // sending command
277            if (tc.getNceProgMode() == false) {
278                return null;
279            }
280            // not supported by USB connected to SB3 or PH
281            if (tc.getUsbSystem() == NceTrafficController.USB_SYSTEM_SB3 ||
282                    tc.getUsbSystem() == NceTrafficController.USB_SYSTEM_SB5 ||
283                    tc.getUsbSystem() == NceTrafficController.USB_SYSTEM_POWERPRO) {
284                log.error("attempt to send unsupported binary command EXIT_PROG_CMD to NCE USB");
285                //       return null;
286            }
287            tc.setNceProgMode(false);
288            m.setBinary(true);
289            m.setReplyLen(REPLY_1);
290            m.setOpCode(EXIT_PROG_CMD);
291            m.setTimeout(SHORT_TIMEOUT);
292        } else {
293            m.setBinary(false);
294            m.setOpCode('X');
295            m.setTimeout(SHORT_TIMEOUT);
296        }
297        return m;
298    }
299
300    /**
301     * Read Paged mode CV on programming track.
302     *
303     * @param tc controller for the associated connection
304     * @param cv the CV to read
305     * @return a new message to read a CV
306     */
307    @Nonnull
308    public static NceMessage getReadPagedCV(@Nonnull NceTrafficController tc, int cv) {
309        // test if supported on current connection
310        if (tc.getUsbSystem() != NceTrafficController.USB_SYSTEM_NONE &&
311                (tc.getCmdGroups() & NceTrafficController.CMDS_PROGTRACK) != NceTrafficController.CMDS_PROGTRACK) {
312            log.error("attempt to send unsupported binary command READ_PAGED_CV_CMD to NCE USB");
313            //   return null;
314        }
315        if (tc.getCommandOptions() >= NceTrafficController.OPTION_2006) {
316            NceMessage m = new NceMessage(3);
317            m.setBinary(true);
318            m.setReplyLen(REPLY_2);
319            m.setOpCode(READ_PAGED_CV_CMD);
320            m.setElement(1, (cv >> 8));
321            m.setElement(2, (cv & 0x0FF));
322            m.setNeededMode(jmri.jmrix.AbstractMRTrafficController.PROGRAMINGMODE);
323            m.setTimeout(NCE_PAGED_CV_TIMEOUT);
324            return m;
325        } else {
326            NceMessage m = new NceMessage(4);
327            m.setBinary(false);
328            m.setOpCode('R');
329            m.addIntAsThree(cv, 1);
330            m.setNeededMode(jmri.jmrix.AbstractMRTrafficController.PROGRAMINGMODE);
331            m.setTimeout(NCE_PAGED_CV_TIMEOUT);
332            return m;
333        }
334    }
335
336    /**
337     * Write paged mode CV to programming track.
338     *
339     * @param tc controller for the associated connection
340     * @param cv CV to write
341     * @param val value to write to cv
342     * @return a new message to write a CV
343     */
344    @Nonnull
345    public static NceMessage getWritePagedCV(@Nonnull NceTrafficController tc, int cv, int val) {
346        // test if supported on current connection
347        if (tc.getUsbSystem() != NceTrafficController.USB_SYSTEM_NONE &&
348                (tc.getCmdGroups() & NceTrafficController.CMDS_PROGTRACK) != NceTrafficController.CMDS_PROGTRACK) {
349            log.error("attempt to send unsupported binary command WRITE_PAGED_CV_CMD to NCE USB");
350            //   return null;
351        }
352        if (tc.getCommandOptions() >= NceTrafficController.OPTION_2006) {
353            NceMessage m = new NceMessage(4);
354            m.setBinary(true);
355            m.setReplyLen(REPLY_1);
356            m.setOpCode(WRITE_PAGED_CV_CMD);
357            m.setElement(1, cv >> 8);
358            m.setElement(2, cv & 0xFF);
359            m.setElement(3, val);
360            m.setNeededMode(jmri.jmrix.AbstractMRTrafficController.PROGRAMINGMODE);
361            m.setTimeout(NCE_PAGED_CV_TIMEOUT);
362            return m;
363        } else {
364            NceMessage m = new NceMessage(8);
365            m.setBinary(false);
366            m.setOpCode('P');
367            m.addIntAsThree(cv, 1);
368            m.setElement(4, ' ');
369            m.addIntAsThree(val, 5);
370            m.setNeededMode(jmri.jmrix.AbstractMRTrafficController.PROGRAMINGMODE);
371            m.setTimeout(NCE_PAGED_CV_TIMEOUT);
372            return m;
373        }
374    }
375
376    @CheckForNull
377    public static NceMessage getReadRegister(@Nonnull NceTrafficController tc, int reg) {
378        // not supported by USB connected to SB3 or PH
379        if (tc.getUsbSystem() == NceTrafficController.USB_SYSTEM_SB3 ||
380                tc.getUsbSystem() == NceTrafficController.USB_SYSTEM_SB5 ||
381                tc.getUsbSystem() == NceTrafficController.USB_SYSTEM_POWERPRO) {
382            log.error("attempt to send unsupported binary command READ_REG_CMD to NCE USB");
383            return null;
384        }
385        if (reg > 8) {
386            log.error("register number too large: {}", reg);
387        }
388        if (tc.getCommandOptions() >= NceTrafficController.OPTION_2006) {
389            NceMessage m = new NceMessage(2);
390            m.setBinary(true);
391            m.setReplyLen(REPLY_2);
392            m.setOpCode(READ_REG_CMD);
393            m.setElement(1, reg);
394            m.setNeededMode(jmri.jmrix.AbstractMRTrafficController.PROGRAMINGMODE);
395            m.setTimeout(NCE_PAGED_CV_TIMEOUT);
396            return m;
397        } else {
398            NceMessage m = new NceMessage(2);
399            m.setBinary(false);
400            m.setOpCode('V');
401            String s = "" + reg;
402            m.setElement(1, s.charAt(s.length() - 1));
403            m.setNeededMode(jmri.jmrix.AbstractMRTrafficController.PROGRAMINGMODE);
404            m.setTimeout(NCE_PAGED_CV_TIMEOUT);
405            return m;
406        }
407    }
408
409    public static NceMessage getWriteRegister(NceTrafficController tc, int reg, int val) {
410        // not supported by USB connected to SB3 or PH
411        if (tc.getUsbSystem() == NceTrafficController.USB_SYSTEM_SB3 ||
412                tc.getUsbSystem() == NceTrafficController.USB_SYSTEM_SB5 ||
413                tc.getUsbSystem() == NceTrafficController.USB_SYSTEM_POWERPRO) {
414            log.error("attempt to send unsupported binary command WRITE_REG_CMD to NCE USB");
415            return null;
416        }
417        if (reg > 8) {
418            log.error("register number too large: {}", reg);
419        }
420        if (tc.getCommandOptions() >= NceTrafficController.OPTION_2006) {
421            NceMessage m = new NceMessage(3);
422            m.setBinary(true);
423            m.setReplyLen(REPLY_1);
424            m.setOpCode(WRITE_REG_CMD);
425            m.setElement(1, reg);
426            m.setElement(2, val);
427            m.setNeededMode(jmri.jmrix.AbstractMRTrafficController.PROGRAMINGMODE);
428            m.setTimeout(NCE_PAGED_CV_TIMEOUT);
429            return m;
430        } else {
431            NceMessage m = new NceMessage(6);
432            m.setBinary(false);
433            m.setOpCode('S');
434            String s = "" + reg;
435            m.setElement(1, s.charAt(s.length() - 1));
436            m.setElement(2, ' ');
437            m.addIntAsThree(val, 3);
438            m.setNeededMode(jmri.jmrix.AbstractMRTrafficController.PROGRAMINGMODE);
439            m.setTimeout(NCE_PAGED_CV_TIMEOUT);
440            return m;
441        }
442    }
443
444    public static NceMessage getReadDirectCV(NceTrafficController tc, int cv) {
445        // not supported by USB connected to SB3 or PH
446        if (tc.getUsbSystem() == NceTrafficController.USB_SYSTEM_SB3 ||
447                tc.getUsbSystem() == NceTrafficController.USB_SYSTEM_SB5 ||
448                tc.getUsbSystem() == NceTrafficController.USB_SYSTEM_POWERPRO) {
449            log.error("attempt to send unsupported binary command READ_DIR_CV_CMD to NCE USB");
450            return null;
451        }
452        if (tc.getCommandOptions() < NceTrafficController.OPTION_2006) {
453            log.error("getReadDirectCV with option {}", tc.getCommandOptions());
454            return null;
455        }
456        NceMessage m = new NceMessage(3);
457        m.setBinary(true);
458        m.setReplyLen(REPLY_2);
459        m.setOpCode(READ_DIR_CV_CMD);
460        m.setElement(1, (cv >> 8));
461        m.setElement(2, (cv & 0x0FF));
462        m.setNeededMode(jmri.jmrix.AbstractMRTrafficController.PROGRAMINGMODE);
463        m.setTimeout(NCE_DIRECT_CV_TIMEOUT);
464        return m;
465    }
466
467    public static NceMessage getWriteDirectCV(NceTrafficController tc, int cv, int val) {
468        // not supported by USB connected to SB3 or PH
469        if (tc.getUsbSystem() == NceTrafficController.USB_SYSTEM_SB3 ||
470                tc.getUsbSystem() == NceTrafficController.USB_SYSTEM_SB5 ||
471                tc.getUsbSystem() == NceTrafficController.USB_SYSTEM_POWERPRO) {
472            log.error("attempt to send unsupported binary command WRITE_DIR_CV_CMD to NCE USB");
473            return null;
474        }
475        if (tc.getCommandOptions() < NceTrafficController.OPTION_2006) {
476            log.error("getWriteDirectCV with option {}", tc.getCommandOptions());
477        }
478        NceMessage m = new NceMessage(4);
479        m.setBinary(true);
480        m.setReplyLen(REPLY_1);
481        m.setOpCode(WRITE_DIR_CV_CMD);
482        m.setElement(1, cv >> 8);
483        m.setElement(2, cv & 0xFF);
484        m.setElement(3, val);
485        m.setNeededMode(jmri.jmrix.AbstractMRTrafficController.PROGRAMINGMODE);
486        m.setTimeout(NCE_DIRECT_CV_TIMEOUT);
487        return m;
488    }
489    
490    public static NceMessage getEpromVersion(NceTrafficController tc) {
491        byte[] bl = NceBinaryCommand.getNceEpromRev();
492        NceMessage m = NceMessage.createBinaryMessage(tc, bl, REPLY_3);
493        return m;
494    }
495    
496    public static NceMessage sendLocoCmd(NceTrafficController tc, int locoAddr, byte locoSubCmd, byte locoData) {
497        byte[] bl = NceBinaryCommand.nceLocoCmd(locoAddr, locoSubCmd, locoData);
498        NceMessage m = NceMessage.createBinaryMessage(tc, bl, REPLY_1);
499        return m;
500    }
501
502    public static NceMessage sendPacketMessage(NceTrafficController tc, byte[] bytes) {
503        NceMessage m = sendPacketMessage(tc, bytes, 2);
504        return m;
505    }
506
507    public static NceMessage sendPacketMessage(NceTrafficController tc, byte[] bytes, int retries) {
508        // this command isn't supported by the NCE USB
509        if (tc.getUsbSystem() != NceTrafficController.USB_SYSTEM_NONE) {
510            log.error("attempt to send unsupported sendPacketMessage to NCE USB cmd: 0x{}", Integer.toHexString(SENDn_BYTES_CMD + bytes.length));
511            return null;
512        }
513        if (tc.getCommandOptions() >= NceTrafficController.OPTION_1999) {
514            if (bytes.length < 3 || bytes.length > 6) {
515                log.error("Send of NCE track packet too short or long:{} packet:{}", Integer.toString(bytes.length), Arrays.toString(bytes));
516            }
517            NceMessage m = new NceMessage(2 + bytes.length);
518            m.setBinary(true);
519            m.setTimeout(SHORT_TIMEOUT);
520            m.setReplyLen(1);
521            int i = 0; // counter to make it easier to format the message
522
523            m.setElement(i++, SENDn_BYTES_CMD + bytes.length);
524            m.setElement(i++, retries); // send this many retries. 
525            for (int j = 0; j < bytes.length; j++) {
526                m.setElement(i++, bytes[j] & 0xFF);
527            }
528            return m;
529        } else {
530            NceMessage m = new NceMessage(5 + 3 * bytes.length);
531            m.setBinary(false);
532            int i = 0; // counter to make it easier to format the message
533
534            m.setElement(i++, 'S'); // "S C02 " means sent it twice
535            m.setElement(i++, ' ');
536            m.setElement(i++, 'C');
537            m.setElement(i++, '0');
538            m.setElement(i++, '2');
539
540            for (int j = 0; j < bytes.length; j++) {
541                m.setElement(i++, ' ');
542                m.addIntAsTwoHex(bytes[j] & 0xFF, i);
543                i = i + 2;
544            }
545            m.setTimeout(SHORT_TIMEOUT);
546            return m;
547        }
548    }
549
550    public static NceMessage createBinaryMessage(NceTrafficController tc, byte[] bytes) {
551        return createBinaryMessage(tc, bytes, REPLY_1);
552    }
553
554    public static NceMessage createBinaryMessage(NceTrafficController tc, byte[] bytes, int replyLen) {
555        if (tc.getCommandOptions() < NceTrafficController.OPTION_2004) {
556            log.error("Attempt to send NCE command to EPROM built before 2004");
557        }
558        if (bytes.length < 1 || bytes.length > 20) {
559            log.error("NCE command message length error:{}", bytes.length);
560        }
561
562        NceMessage m = new NceMessage(bytes.length);
563        m.setBinary(true);
564        m.setReplyLen(replyLen);
565        m.setTimeout(SHORT_TIMEOUT);
566
567        for (int j = 0; j < bytes.length; j++) {
568            m.setElement(j, bytes[j] & 0xFF);
569        }
570        return m;
571    }
572
573    public static NceMessage queuePacketMessage(NceTrafficController tc, byte[] bytes) {
574        // this command isn't supported by the NCE USB
575        if (tc.getUsbSystem() != NceTrafficController.USB_SYSTEM_NONE) {
576            log.error("attempt to send unsupported queuePacketMessage to NCE USB");
577            return null;
578        }
579        if (tc.getCommandOptions() >= NceTrafficController.OPTION_1999) {
580            if (bytes.length < 3 || bytes.length > 6) {
581                log.error("Queue of NCE track packet too long:{} packet :{}", Integer.toString(bytes.length), Arrays.toString(bytes));
582            }
583            NceMessage m = new NceMessage(1 + bytes.length);
584            m.setBinary(true);
585            m.setReplyLen(REPLY_1);
586            int i = 0; // counter to make it easier to format the message
587
588            m.setElement(i++, QUEUEn_BYTES_CMD + bytes.length);
589            for (int j = 0; j < bytes.length; j++) {
590                m.setElement(i++, bytes[j] & 0xFF);
591            }
592            return m;
593        } else {
594            NceMessage m = new NceMessage(1 + 3 * bytes.length);
595            m.setBinary(false);
596            int i = 0; // counter to make it easier to format the message
597
598            m.setElement(i++, 'Q'); // "S C02 " means sent it twice
599
600            for (int j = 0; j < bytes.length; j++) {
601                m.setElement(i++, ' ');
602                m.addIntAsTwoHex(bytes[j] & 0xFF, i);
603                i = i + 2;
604            }
605            return m;
606        }
607    }
608
609    public static NceMessage createAccySignalMacroMessage(NceTrafficController tc, int op, int addr, int data) {
610        if (tc.getCommandOptions() < NceTrafficController.OPTION_2004) {
611            log.error("Attempt to send NCE command to EPROM built before 2004");
612        }
613        NceMessage m = new NceMessage(5);
614        m.setBinary(true);
615        m.setReplyLen(REPLY_1);
616        m.setTimeout(SHORT_TIMEOUT);
617        m.setOpCode(SEND_ACC_SIG_MACRO_CMD);
618        m.setElement(1, (addr >> 8) & 0xFF);
619        m.setElement(2, addr & 0xFF);
620        m.setElement(3, op);
621        m.setElement(4, data);
622        return m;
623    }
624
625    public static NceMessage createAccDecoderPktOpsMode(NceTrafficController tc, int accyAddr, int cvAddr, int cvData) {
626        NceMessage m = new NceMessage(6);
627        m.setBinary(true);
628        m.setReplyLen(REPLY_1);
629        m.setTimeout(SHORT_TIMEOUT);
630        byte[] mess = NceBinaryCommand.usbOpsModeAccy(accyAddr, cvAddr, cvData);
631        m.setOpCode(mess[0]);
632        m.setElement(1, mess[1]);
633        m.setElement(2, mess[2]);
634        m.setElement(3, mess[3]);
635        m.setElement(4, mess[4]);
636        m.setElement(5, mess[5]);
637        return m;
638    }
639
640    /**
641     * {@inheritDoc}
642     */
643    @Override
644    public String toMonitorString() {
645        return NceMonBinary.INSTANCE.displayMessage(this);
646    }
647}