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