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