001package jmri.jmrix.nce;
002
003import jmri.NmraPacket;
004import org.slf4j.Logger;
005import org.slf4j.LoggerFactory;
006
007/*
008 
009 From NCE System notes for version March 1, 2007
010 
011 New 0xAD command sends accessory or signal packets.
012 This command can also issue NCE macros
013 Command Format: 0xAD <addr_h> <addr_l> <op_1> <data_1>
014 Addr_h and Addr_l are the accessory/signal address as a
015 normal binary number (NOT in DCC format).
016 Ex: Accessory Address 1 = 0x00 0x01 (hi byte first)
017 Ex: Accessory Address 2 = 0x00 0x02 (hi byte first)
018 Ex: Accessory Address 513 = 0x02 0x01 (hi byte first)
019 NOTE: accy/signal address 0 is not a valid address
020 
021 Op_1   Data_1       Operation description
022 01     0-255        NCE macro number 0-255
023 02     0-255        Duplicate of Op_1 command 01
024 03     0            Accessory Normal direction (ON)
025 04     0            Accessory Reverse direction (OFF)
026 05     0-1f         Signal Aspect 0-31
027 06-7f               reserved reserved
028 
029 Returns: ! = success
030 1 = bad accy/signal address
031 
032 0xA2 sends speed or function packets to a locomotive.
033 
034 Command Format: 0xA2 <addr_h> <addr_l> <op_1> <data_1>
035 Addr_h and Addr_l are the loco address in DCC format.
036 If a long address is in use, bits 6 and 7 of the high byte are set.
037 Example: Long address 3 = 0xc0 0x03
038 Short address 3 = 0x00 0x03
039  
040 Op_1   Data_1       Operation description
041 01     0-7f         Reverse 28 speed command
042 02     0-7f         Forward 28 speed command
043 03     0-7f         Reverse 128 speed command
044 04     0-7f         Forward 128 speed command
045 05     0            Estop reverse command
046 06     0            Estop forward command
047 07     0-1f         Function group 1 (same format as DCC packet for FG1
048 08     0-0f         Function group 2 (same format as DCC packet for FG2
049 09     0-0f         Function group 3 (same format as DCC packet for FG3
050 0a     0-7f         Set reverse consist address for lead loco
051 0b     0-7f         Set forward consist address for lead loco
052 0c     0-7f         Set reverse consist address for rear loco
053 0d     0-7f         Set forward consist address for rear loco
054 0e     0-7f         Set reverse consist address for additional loco
055 0f     0-7f         Set forward consist address for additional loco
056 10     0            Del loco from consist
057 11     0            Kill consist
058 12     0-9          Set momentum
059 13     0-7f         No action, always returns success
060 14     0-7f         No action, always returns success
061 15     0-ff         Functions 13-20 control (bit 0=F13, bit 7=F20)
062 16     0-ff         Functions 21-28 control (bit 0=F21, bit 7=F28)
063 17     0-3f         Assign this loco to cab number in data_1
064 18-7f               reserved reserved
065  
066 Returns: ! = success
067 1 = bad loco address
068   
069 */
070/**
071 * NCE Binary Commands
072 *
073 * Also see NceMessage.java for additional commands
074 *
075 * @author Daniel Boudreau (C) 2007, 2010
076 * @author ken cameron (C) 2013
077 */
078public class NceBinaryCommand {
079
080// all commands moved to NceMessage 07/17/2018 DAB
081//    public static final int NOP_CMD = 0x80;            // NCE No Op Command, NCE-USB yes
082//    public static final int ASSIGN_CAB_CMD = 0x81;      // NCE Assign loco to cab command, NCE-USB no
083//    public static final int READ_CLOCK_CMD = 0x82;      // NCE read clock command, NCE-USB no
084//    public static final int STOP_CLOCK_CMD = 0x83;      // NCE stop clock command, NCE-USB no
085//    public static final int START_CLOCK_CMD = 0x84;     // NCE start clock command, NCE-USB no
086//    public static final int SET_CLOCK_CMD = 0x85;       // NCE set clock command, NCE-USB no
087//    public static final int CLOCK_1224_CMD = 0x86;      // NCE change clock 12/24 command, NCE-USB no
088//    public static final int CLOCK_RATIO_CMD = 0x87;     // NCE set clock ratio command, NCE-USB no
089//    public static final int DEQUEUE_CMD = 0x88;         // NCE dequeue packets based on loco addr, NCE-USB no
090//    public static final int ENABLE_TRACK_CMD = 0x89;    // NCE enable track/kill programm track, NCE-USB no
091//    public static final int READ_AUI4_CMD = 0x8A;       // NCE read status of AUI yy, returns four bytes, NCE-USB no
092//    public static final int DISABLE_TRACK_CMD = 0x89;   // NCE enable program/kill main track, NCE-USB no
093//    public static final int DUMMY_CMD = 0x8C;           // NCE Dummy instruction, NCE-USB yes
094//    public static final int SPEED_MODE_CMD = 0x8D;      // NCE set speed mode, NCE-USB no
095//    public static final int WRITE_N_CMD = 0x8E;         // NCE write up to 16 bytes of memory command, NCE-USB no
096//    public static final int READ16_CMD = 0x8F;          // NCE read 16 bytes of memory command, NCE-USB no
097//    public static final int DISPLAY3_CMD = 0x90;        // NCE write 16 char to cab display line 3, NCE-USB no
098//    public static final int DISPLAY4_CMD = 0x91;        // NCE write 16 char to cab display line 4, NCE-USB no
099//    public static final int DISPLAY2_CMD = 0x92;        // NCE write 8 char to cab display line 2 right, NCE-USB no
100//    public static final int QUEUE3_TMP_CMD = 0x93;      // NCE queue 3 bytes to temp queue, NCE-USB no
101//    public static final int QUEUE4_TMP_CMD = 0x94;      // NCE queue 4 bytes to temp queue, NCE-USB no
102//    public static final int QUEUE5_TMP_CMD = 0x95;      // NCE queue 5 bytes to temp queue, NCE-USB no
103//    public static final int QUEUE6_TMP_CMD = 0x96;      // NCE queue 6 bytes to temp queue, NCE-USB no
104//    public static final int WRITE1_CMD = 0x97;          // NCE write 1 bytes of memory command, NCE-USB no
105//    public static final int WRITE2_CMD = 0x98;          // NCE write 2 bytes of memory command, NCE-USB no
106//    public static final int WRITE4_CMD = 0x99;          // NCE write 4 bytes of memory command, NCE-USB no
107//    public static final int WRITE8_CMD = 0x9A;          // NCE write 8 bytes of memory command, NCE-USB no
108//    public static final int READ_AUI2_CMD = 0x9B;       // NCE read status of AUI yy, returns two bytes, NCE-USB >= 1.65
109//    public static final int MACRO_CMD = 0x9C;           // NCE execute macro n, NCE-USB yes
110//    public static final int READ1_CMD = 0x9D;           // NCE read 1 byte of memory command, NCE-USB no
111//    public static final int PGM_TRK_ON_CMD = 0x9E;      // NCE enter program track  command, NCE-USB yes
112//    public static final int PGM_TRK_OFF_CMD = 0x9F;     // NCE exit program track  command, NCE-USB yes
113//    public static final int PGM_PAGE_WRITE_CMD = 0xA0;  // NCE program track, page mode write command, NCE-USB yes
114//    public static final int PGM_PAGE_READ_CMD = 0xA1;   // NCE program track, page mode read command, NCE-USB yes
115//    public static final int LOCO_CMD = 0xA2;            // NCE loco control command, NCE-USB yes
116//    public static final int QUEUE3_TRK_CMD = 0xA3;      // NCE queue 3 bytes to track queue, NCE-USB no
117//    public static final int QUEUE4_TRK_CMD = 0xA4;      // NCE queue 4 bytes to track queue, NCE-USB no
118//    public static final int QUEUE5_TRK_CMD = 0xA5;      // NCE queue 5 bytes to track queue, NCE-USB no
119//    public static final int PGM_REG_WRITE_CMD = 0xA6;   // NCE program track, register mode write command, NCE-USB yes
120//    public static final int PGM_REG_READ_CMD = 0xA7;    // NCE program track, register mode read command, NCE-USB yes
121//    public static final int PGM_DIR_WRITE_CMD = 0xA8;   // NCE program track, direct mode write command, NCE-USB yes
122//    public static final int PGM_DIR_READ_CMD = 0xA9;    // NCE program track, direct mode read command, NCE-USB yes
123//    public static final int SW_REV_CMD = 0xAA;          // NCE get EPROM revision cmd, Reply Format: VV.MM.mm, NCE-USB yes
124//    public static final int RESET_SOFT_CMD = 0xAB;      // NCE soft reset command, NCE-USB no
125//    public static final int RESET_HARD_CMD = 0xAC;      // NCE hard reset command, NCE-USB no
126//    public static final int ACC_CMD = 0xAD;             // NCE accessory command, NCE-USB yes
127//    public static final int OPS_PROG_LOCO_CMD = 0xAE;   // NCE ops mode program loco, NCE-USB yes
128//    public static final int OPS_PROG_ACCY_CMD = 0xAF;   // NCE ops mode program accessories, NCE-USB yes
129//    public static final int FACTORY_TEST_CMD = 0xB0;    // NCE factory test, NCE-USB yes
130//    public static final int USB_SET_CAB_CMD = 0xB1;     // NCE set cab address in USB, NCE-USB yes
131//    public static final int USB_MEM_POINTER_CMD = 0xB3; // NCE set memory context pointer, NCE-USB >= 1.65
132//    public static final int USB_MEM_WRITE_CMD = 0xB4;   // NCE write memory, NCE-USB >= 1.65
133//    public static final int USB_MEM_READ_CMD = 0xB5;    // NCE read memory, NCE-USB >= 1.65
134
135    // NCE Command 0xA2 sends speed or function packets to a locomotive
136    // 0xA2 sub commands speed and functions
137    public static final byte LOCO_CMD_REV_28SPEED = 0x01;  // set loco speed 28 steps reverse
138    public static final byte LOCO_CMD_FWD_28SPEED = 0x02;  // set loco speed 28 steps forward
139    public static final byte LOCO_CMD_REV_128SPEED = 0x03; // set loco speed 128 steps reverse
140    public static final byte LOCO_CMD_FWD_128SPEED = 0x04; // set loco speed 128 steps forward
141    public static final byte LOCO_CMD_REV_ESTOP = 0x05;    // emergency stop reverse
142    public static final byte LOCO_CMD_FWD_ESTOP = 0x06;    // emergency stop forward
143    public static final byte LOCO_CMD_FG1 = 0x07;          // function group 1
144    public static final byte LOCO_CMD_FG2 = 0x08;          // function group 2
145    public static final byte LOCO_CMD_FG3 = 0x09;          // function group 3
146    public static final byte LOCO_CMD_FG4 = 0x15;          // function group 4
147    public static final byte LOCO_CMD_FG5 = 0x16;          // function group 5
148
149    // OxA2 sub commands consist
150    public static final byte LOCO_CMD_REV_CONSIST_LEAD = 0x0A;    // reverse consist address for lead loco
151    public static final byte LOCO_CMD_FWD_CONSIST_LEAD = 0x0B;    // forward consist address for lead loco 
152    public static final byte LOCO_CMD_REV_CONSIST_REAR = 0x0C;    // reverse consist address for rear loco 
153    public static final byte LOCO_CMD_FWD_CONSIST_REAR = 0x0D;    // forward consist address for rear loco
154    public static final byte LOCO_CMD_REV_CONSIST_MID = 0x0E;     // reverse consist address for additional loco 
155    public static final byte LOCO_CMD_FWD_CONSIST_MID = 0x0F;     // forward consist address for additional loco 
156    public static final byte LOCO_CMD_DELETE_LOCO_CONSIST = 0x10; // Delete loco from consist
157    public static final byte LOCO_CMD_KILL_CONSIST = 0x11;        // Kill consist
158
159    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "PZLA_PREFER_ZERO_LENGTH_ARRAYS",
160        justification = "Long-standing API, risky to update")
161    public static byte[] accDecoder(int number, boolean closed) {
162
163        if (number < NmraPacket.accIdLowLimit || number > NmraPacket.accIdAltHighLimit) {
164            log.error("invalid NCE accessory address {}", number);
165            return null;
166        }
167        byte op_1;
168        if (closed) {
169            op_1 = 0x03;
170        } else {
171            op_1 = 0x04;
172        }
173
174        int addr_h = number / 256;
175        int addr_l = number & 0xFF;
176
177        byte[] retVal = new byte[5];
178        retVal[0] = (byte) (NceMessage.SEND_ACC_SIG_MACRO_CMD); // NCE accessory command
179        retVal[1] = (byte) (addr_h);  // high address
180        retVal[2] = (byte) (addr_l);  // low address
181        retVal[3] = op_1;             // command
182        retVal[4] = (byte) 0;         // zero out last byte for acc
183
184        return retVal;
185    }
186
187    public static byte[] accMemoryRead(int address) {
188
189        int addr_h = address / 256;
190        int addr_l = address & 0xFF;
191
192        byte[] retVal = new byte[3];
193        retVal[0] = (byte) (NceMessage.READ16_CMD); // read 16 bytes command
194        retVal[1] = (byte) (addr_h);     // high address
195        retVal[2] = (byte) (addr_l);     // low address
196
197        return retVal;
198    }
199
200    /**
201     * Read one byte from NCE command station memory
202     *
203     * @param address address to read from
204     * @return binary command to read one byte
205     */
206    public static byte[] accMemoryRead1(int address) {
207
208        int addr_h = address / 256;
209        int addr_l = address & 0xFF;
210
211        byte[] retVal = new byte[3];
212        retVal[0] = (byte) (NceMessage.READ1_CMD); // read 1 byte command
213        retVal[1] = (byte) (addr_h);    // high address
214        retVal[2] = (byte) (addr_l);    // low address
215
216        return retVal;
217    }
218
219    private final static int BUFFER_SIZE_16 = 16;
220    public static byte[] accMemoryWriteN(int address, byte[] data) {
221
222        int addr_h = address / 256;
223        int addr_l = address & 0xFF;
224
225        byte[] retVal = new byte[4 + BUFFER_SIZE_16];
226        int j = 0;
227        retVal[j++] = (byte) (NceMessage.WRITE_N_CMD); // write n bytes command
228        retVal[j++] = (byte) (addr_h);      // high address
229        retVal[j++] = (byte) (addr_l);      // low address
230        retVal[j++] = (byte) data.length;   // number of bytes to write
231        
232        for (int i = 0; i < data.length; i++, j++) {
233            retVal[j] = data[i];
234        }
235
236        return retVal;
237    }
238
239    private final static int BUFFER_SIZE_8 = 8;
240    public static byte[] accMemoryWrite8(int address, byte[] data) {
241
242        int addr_h = address / 256;
243        int addr_l = address & 0xFF;
244
245        byte[] retVal = new byte[3 + BUFFER_SIZE_8];
246        int j = 0;
247        retVal[j++] = (byte) (NceMessage.WRITE8_CMD); // write 8 bytes command
248        retVal[j++] = (byte) (addr_h);     // high address
249        retVal[j++] = (byte) (addr_l);     // low address
250        
251        for (int i = 0; i < data.length; i++, j++) {
252            retVal[j] = data[i];
253        }
254
255        return retVal;
256    }
257
258    private final static int BUFFER_SIZE_4 = 4;
259    public static byte[] accMemoryWrite4(int address, byte[] data) {
260
261        int addr_h = address / 256;
262        int addr_l = address & 0xFF;
263
264        byte[] retVal = new byte[3 + BUFFER_SIZE_4];
265        int j = 0;
266        retVal[j++] = (byte) (NceMessage.WRITE4_CMD); // write 4 bytes command
267        retVal[j++] = (byte) (addr_h);     // high address
268        retVal[j++] = (byte) (addr_l);     // low address
269        
270        for (int i = 0; i < data.length; i++, j++) {
271            retVal[j] = data[i];
272        }
273
274        return retVal;
275    }
276
277    private final static int BUFFER_SIZE_2 = 2;
278    public static byte[] accMemoryWrite2(int address, byte[] data) {
279
280        int addr_h = address / 256;
281        int addr_l = address & 0xFF;
282
283        byte[] retVal = new byte[3 + BUFFER_SIZE_2];
284        int j = 0;
285        retVal[j++] = (byte) (NceMessage.WRITE2_CMD); // write 4 bytes command
286        retVal[j++] = (byte) (addr_h);     // high address
287        retVal[j++] = (byte) (addr_l);     // low address
288        
289        for (int i = 0; i < data.length; i++, j++) {
290            retVal[j] = data[i];
291        }
292
293        return retVal;
294    }
295
296    public static byte[] accMemoryWrite1(int address, byte data) {
297
298        int addr_h = address / 256;
299        int addr_l = address & 0xFF;
300
301        byte[] retVal = new byte[3 + 1];
302        retVal[0] = (byte) (NceMessage.WRITE1_CMD); // write 4 bytes command
303        retVal[1] = (byte) (addr_h);     // high address
304        retVal[2] = (byte) (addr_l);     // low address
305        retVal[3] = data;
306
307        return retVal;
308    }
309
310    public static byte[] accAiu2Read(int cabId) {
311
312        byte[] retVal = new byte[1 + 1];
313        retVal[0] = (byte) (NceMessage.READ_AUI2_CMD); // write 4 bytes command
314        retVal[1] = (byte) (cabId);         // cab address
315
316        return retVal;
317    }
318
319    public static byte[] usbSetCabId(int cab) {
320
321        byte[] retVal = new byte[2];
322        retVal[0] = (byte) (NceMessage.USB_SET_CAB_CMD); // read N bytes command
323        retVal[1] = (byte) (cab);             // cab number
324
325        return retVal;
326    }
327
328    public static byte[] usbMemoryWrite1(byte data) {
329
330        byte[] retVal = new byte[2];
331        retVal[0] = (byte) (NceMessage.USB_MEM_WRITE_CMD); // write 2 bytes command
332        retVal[1] = (data);                     // data
333
334        return retVal;
335    }
336
337    public static byte[] usbMemoryRead(int num) {
338
339        byte[] retVal = new byte[2];
340        retVal[0] = (byte) (NceMessage.USB_MEM_READ_CMD); // read N bytes command
341        retVal[1] = (byte) (num);              // byte count
342
343        return retVal;
344    }
345
346    public static byte[] usbMemoryPointer(int cab, int loc) {
347
348        byte[] retVal = new byte[3];
349        retVal[0] = (byte) (NceMessage.USB_MEM_POINTER_CMD); // read N bytes command
350        retVal[1] = (byte) (cab);                 // cab number
351        retVal[2] = (byte) (loc);                 // memory offset
352
353        return retVal;
354    }
355
356    public static byte[] accStopClock() {
357
358        byte[] retVal = new byte[1];
359        retVal[0] = (byte) (NceMessage.STOP_CLOCK_CMD); // stop clock command
360
361        return retVal;
362    }
363
364    public static byte[] accStartClock() {
365
366        byte[] retVal = new byte[1];
367        retVal[0] = (byte) (NceMessage.START_CLOCK_CMD); // start clock command
368
369        return retVal;
370    }
371
372    public static byte[] accSetClock(int hours, int minutes) {
373
374        byte[] retVal = new byte[3];
375        retVal[0] = (byte) (NceMessage.SET_CLOCK_CMD); // set clock command
376        retVal[1] = (byte) (hours);         // hours
377        retVal[2] = (byte) (minutes);       // minutes
378
379        return retVal;
380    }
381
382    public static byte[] accSetClock1224(boolean flag) {
383
384        int bit = 0;
385        if (flag) {
386            bit = 1;
387        }
388        byte[] retVal = new byte[2];
389        retVal[0] = (byte) (NceMessage.CLOCK_1224_CMD); // set clock 12/24 command
390        retVal[1] = (byte) (bit);            // 12 - 0, 24 - 1
391
392        return retVal;
393    }
394
395    public static byte[] accSetClockRatio(int ratio) {
396
397        byte[] retVal = new byte[2];
398        retVal[0] = (byte) (NceMessage.CLOCK_RATIO_CMD); // set clock command
399        retVal[1] = (byte) (ratio);           // fast clock ratio
400
401        return retVal;
402    }
403
404    @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "PZLA_PREFER_ZERO_LENGTH_ARRAYS",
405        justification = "Long-standing API, risky to update")
406    public static byte[] nceLocoCmd(int locoAddr, byte locoSubCmd, byte locoData) {
407        if (locoSubCmd < 1 || locoSubCmd > 0x17) {
408            log.error("invalid NCE loco command {}", locoSubCmd);
409            return null;
410        }
411
412        int locoAddr_h = locoAddr / 256;
413        int locoAddr_l = locoAddr & 0xFF;
414
415        byte[] retVal = new byte[5];
416        retVal[0] = (byte) (NceMessage.LOCO_CMD);   // NCE Loco command
417        retVal[1] = (byte) (locoAddr_h); // loco high address
418        retVal[2] = (byte) (locoAddr_l); // loco low address
419        retVal[3] = locoSubCmd;          // sub command
420        retVal[4] = locoData;            // sub data
421
422        return retVal;
423    }
424
425    /**
426     * Create NCE EPROM revision message. The reply format is:
427     * {@literal VV.MM.mm}
428     *
429     * @return the revision message
430     */
431    public static byte[] getNceEpromRev() {
432        byte[] retVal = new byte[1];
433        retVal[0] = (byte) (NceMessage.SW_REV_CMD);
434        return retVal;
435    }
436
437    /**
438     * Create a NCE USB compatible ops mode loco message.
439     *
440     * @param tc       traffic controller; ignored
441     * @param locoAddr locomotive address
442     * @param cvAddr   CV to set
443     * @param cvData   value to set CV to
444     * @return ops mode message
445     */
446    public static byte[] usbOpsModeLoco(NceTrafficController tc, int locoAddr, int cvAddr, int cvData) {
447
448        byte[] retVal = new byte[6];
449        int locoAddr_h = locoAddr / 256;
450        int locoAddr_l = locoAddr & 0xFF;
451        int cvAddr_h = cvAddr / 256;
452        int cvAddr_l = cvAddr & 0xFF;
453
454        retVal[0] = (byte) (NceMessage.OPS_PROG_LOCO_CMD); // NCE ops mode loco command
455        retVal[1] = (byte) (locoAddr_h);        // loco high address
456        retVal[2] = (byte) (locoAddr_l);        // loco low address
457        retVal[3] = (byte) (cvAddr_h);          // CV high address
458        retVal[4] = (byte) (cvAddr_l);          // CV low address
459        retVal[5] = (byte) (cvData);            // CV data
460
461        return retVal;
462    }
463
464    /**
465     * Create a NCE USB compatible ops mode accessory message.
466     *
467     * @param accyAddr locomotive address
468     * @param cvAddr   CV to set
469     * @param cvData   value to set CV to
470     * @return ops mode message
471     */
472    public static byte[] usbOpsModeAccy(int accyAddr, int cvAddr, int cvData) {
473
474        byte[] retVal = new byte[6];
475        int accyAddr_h = accyAddr / 256;
476        int accyAddr_l = accyAddr & 0xFF;
477        int cvAddr_h = cvAddr / 256;
478        int cvAddr_l = cvAddr & 0xFF;
479
480        retVal[0] = (byte) (NceMessage.OPS_PROG_ACCY_CMD); // NCE ops mode accy command
481        retVal[1] = (byte) (accyAddr_h);        // accy high address
482        retVal[2] = (byte) (accyAddr_l);        // accy low address
483        retVal[3] = (byte) (cvAddr_h);          // CV high address
484        retVal[4] = (byte) (cvAddr_l);          // CV low address
485        retVal[5] = (byte) (cvData);            // CV data
486
487        return retVal;
488    }
489
490    private final static Logger log = LoggerFactory.getLogger(NceBinaryCommand.class);
491}