001package jmri.jmrix.nce.ncemon;
002
003import java.text.MessageFormat;
004
005import org.slf4j.Logger;
006import org.slf4j.LoggerFactory;
007
008import jmri.jmrix.nce.NceMessage;
009import jmri.jmrix.nce.NceReply;
010import jmri.util.StringUtil;
011
012/**
013 * A utility class for formatting NCE binary command and replies into
014 * human-readable text. The text for the display comes from NCE's Bincmds.txt
015 * published November 2007 and is used with NCE's permission.
016 *
017 * @author Daniel Boudreau Copyright (C) 2012
018 * @author Ken Cameron (C) 2023
019 */
020public class NceMonBinary {
021
022    private static final Logger log = LoggerFactory.getLogger(NceMonBinary.class);
023
024    private static final int REPLY_UNKNOWN = 0;
025    private static final int REPLY_STANDARD = 1;
026    private static final int REPLY_DATA = 2;
027    private static final int REPLY_ENTER_PROGRAMMING_MODE = 3;
028
029    // The standard replies
030    private static final int REPLY_ZERO = '0'; // command not supported
031    private static final int REPLY_ONE = '1';  // loco/accy/signal address out of range
032    private static final int REPLY_TWO = '2';  // cab address or op code out of range
033    private static final int REPLY_THREE = '3';// CV address or data out of range
034    private static final int REPLY_FOUR = '4'; // byte count out of range
035    private static final int REPLY_OK = '!';   // command completed successfully
036
037    private int replyType = REPLY_UNKNOWN;
038
039    /**
040     * Creates a command message for the log, in a human-friendly form if
041     * possible.
042     *
043     * @param m the raw command message
044     * @return the displayable message string
045     */
046    public String displayMessage(NceMessage m) {
047        return parseMessage(m);
048    }
049
050    private String parseMessage(NceMessage m) {
051        // first check for messages that have a standard reply
052        replyType = REPLY_STANDARD;
053        switch (m.getOpCode() & 0xFF) {
054            case (NceMessage.NOP_CMD):
055                return Bundle.getMessage("NOP_CMD");
056            case (NceMessage.STOP_CLOCK_CMD):
057                return Bundle.getMessage("STOP_CLOCK_CMD");
058            case (NceMessage.START_CLOCK_CMD):
059                return Bundle.getMessage("START_CLOCK_CMD");
060            case (NceMessage.SET_CLOCK_CMD):
061                if (m.getNumDataElements() == 3) {
062                    return MessageFormat.format(Bundle.getMessage("SET_CLOCK_CMD"),
063                            new Object[]{m.getElement(1), m.getElement(2)});
064                }
065                break;
066            case (NceMessage.CLOCK_1224_CMD):
067                if (m.getNumDataElements() == 2) {
068                    String hr = "12";
069                    if (m.getElement(1) == 1) {
070                        hr = "24";
071                    }
072                    return MessageFormat.format(Bundle.getMessage("CLOCK_1224_CMD"),
073                            new Object[]{hr});
074                }
075                break;
076            case (NceMessage.CLOCK_RATIO_CMD):
077                if (m.getNumDataElements() == 2) {
078                    return MessageFormat.format(Bundle.getMessage("CLOCK_RATIO_CMD"),
079                            new Object[]{m.getElement(1)});
080                }
081                break;
082            case (NceMessage.ENABLE_MAIN_CMD):
083                return Bundle.getMessage("ENABLE_MAIN_CMD");
084            case (NceMessage.KILL_MAIN_CMD):
085                return Bundle.getMessage("KILL_MAIN_CMD");
086            case (NceMessage.WRITE_N_CMD):
087                if (m.getNumDataElements() == 20) {
088                    return MessageFormat.format(Bundle.getMessage("WRITEn_CMD"),
089                            new Object[]{m.getElement(3), getAddress(m), getDataBytes(m, 4, 16)});
090                }
091                break;
092            // Send n bytes commands 0x93 - 0x96
093            case (NceMessage.SENDn_BYTES_CMD + 3):
094                if (m.getNumDataElements() == 5) {
095                    return MessageFormat.format(Bundle.getMessage("SENDn_BYTES_CMD"),
096                            new Object[]{"3", m.getElement(1), getDataBytes(m, 2, 3)});
097                }
098                break;
099            case (NceMessage.SENDn_BYTES_CMD + 4):
100                if (m.getNumDataElements() == 6) {
101                    return MessageFormat.format(Bundle.getMessage("SENDn_BYTES_CMD"),
102                            new Object[]{"4", m.getElement(1), getDataBytes(m, 2, 4)});
103                }
104                break;
105            case (NceMessage.SENDn_BYTES_CMD + 5):
106                if (m.getNumDataElements() == 7) {
107                    return MessageFormat.format(Bundle.getMessage("SENDn_BYTES_CMD"),
108                            new Object[]{"5", m.getElement(1), getDataBytes(m, 2, 5)});
109                }
110                break;
111            case (NceMessage.SENDn_BYTES_CMD + 6):
112                if (m.getNumDataElements() == 8) {
113                    return MessageFormat.format(Bundle.getMessage("SENDn_BYTES_CMD"),
114                            new Object[]{"6", m.getElement(1), getDataBytes(m, 2, 6)});
115                }
116                break;
117            case (NceMessage.WRITE1_CMD):
118                if (m.getNumDataElements() == 4) {
119                    return MessageFormat.format(Bundle.getMessage("WRITE1_CMD"),
120                            new Object[]{getAddress(m), getDataBytes(m, 3, 1)});
121                }
122                break;
123            case (NceMessage.WRITE2_CMD):
124                if (m.getNumDataElements() == 5) {
125                    return MessageFormat.format(Bundle.getMessage("WRITE2_CMD"),
126                            new Object[]{getAddress(m), getDataBytes(m, 3, 2)});
127                }
128                break;
129            case (NceMessage.WRITE4_CMD):
130                if (m.getNumDataElements() == 7) {
131                    return MessageFormat.format(Bundle.getMessage("WRITE4_CMD"),
132                            new Object[]{getAddress(m), getDataBytes(m, 3, 4)});
133                }
134                break;
135            case (NceMessage.WRITE8_CMD):
136                if (m.getNumDataElements() == 11) {
137                    return MessageFormat.format(Bundle.getMessage("WRITE8_CMD"),
138                            new Object[]{getAddress(m), getDataBytes(m, 3, 8)});
139                }
140                break;
141            case (NceMessage.MACRO_CMD):
142                if (m.getNumDataElements() == 2) {
143                    return MessageFormat.format(Bundle.getMessage("MACRO_CMD"),
144                            new Object[]{m.getElement(1)});
145                }
146                break;
147            case (NceMessage.ENTER_PROG_CMD): {
148                replyType = REPLY_ENTER_PROGRAMMING_MODE;
149                return Bundle.getMessage("ENTER_PROG_CMD");
150            }
151            case (NceMessage.EXIT_PROG_CMD):
152                return Bundle.getMessage("EXIT_PROG_CMD");
153            case (NceMessage.WRITE_PAGED_CV_CMD):
154                if (m.getNumDataElements() == 4) {
155                    return MessageFormat.format(Bundle.getMessage("WRITE_PAGED_CV_CMD"),
156                            new Object[]{getNumber(m), getDataBytes(m, 3, 1)});
157                }
158                break;
159            case (NceMessage.LOCO_CMD):
160                if (m.getNumDataElements() == 5) {
161                    // byte three is the Op_1
162                    switch (m.getElement(3)) {
163                        case (0):
164                            return MessageFormat.format(Bundle.getMessage("LOCO_CMD_Op1_00"),
165                                    new Object[]{getLocoAddress(m), m.getElement(4)});
166                        case (1):
167                            return MessageFormat.format(Bundle.getMessage("LOCO_CMD_Op1_01"),
168                                    new Object[]{getLocoAddress(m), m.getElement(4)});
169                        case (2):
170                            return MessageFormat.format(Bundle.getMessage("LOCO_CMD_Op1_02"),
171                                    new Object[]{getLocoAddress(m), m.getElement(4)});
172                        case (3):
173                            return MessageFormat.format(Bundle.getMessage("LOCO_CMD_Op1_03"),
174                                    new Object[]{getLocoAddress(m), m.getElement(4)});
175                        case (4):
176                            return MessageFormat.format(Bundle.getMessage("LOCO_CMD_Op1_04"),
177                                    new Object[]{getLocoAddress(m), m.getElement(4)});
178                        case (5):
179                            return MessageFormat.format(Bundle.getMessage("LOCO_CMD_Op1_05"),
180                                    new Object[]{getLocoAddress(m), m.getElement(4)});
181                        case (6):
182                            return MessageFormat.format(Bundle.getMessage("LOCO_CMD_Op1_06"),
183                                    new Object[]{getLocoAddress(m), m.getElement(4)});
184                        case (7):
185                            return MessageFormat.format(Bundle.getMessage("LOCO_CMD_Op1_07"),
186                                    new Object[]{getLocoAddress(m), m.getElement(4), getFunctionNumber(m)});
187                        case (8):
188                            return MessageFormat.format(Bundle.getMessage("LOCO_CMD_Op1_08"),
189                                    new Object[]{getLocoAddress(m), m.getElement(4), getFunctionNumber(m)});
190                        case (9):
191                            return MessageFormat.format(Bundle.getMessage("LOCO_CMD_Op1_09"),
192                                    new Object[]{getLocoAddress(m), m.getElement(4), getFunctionNumber(m)});
193                        case (0x0A):
194                            return MessageFormat.format(Bundle.getMessage("LOCO_CMD_Op1_0A"),
195                                    new Object[]{getLocoAddress(m), m.getElement(4)});
196                        case (0x0b):
197                            return MessageFormat.format(Bundle.getMessage("LOCO_CMD_Op1_0B"),
198                                    new Object[]{getLocoAddress(m), m.getElement(4)});
199                        case (0x0C):
200                            return MessageFormat.format(Bundle.getMessage("LOCO_CMD_Op1_0C"),
201                                    new Object[]{getLocoAddress(m), m.getElement(4)});
202                        case (0x0D):
203                            return MessageFormat.format(Bundle.getMessage("LOCO_CMD_Op1_0D"),
204                                    new Object[]{getLocoAddress(m), m.getElement(4)});
205                        case (0x0E):
206                            return MessageFormat.format(Bundle.getMessage("LOCO_CMD_Op1_0E"),
207                                    new Object[]{getLocoAddress(m), m.getElement(4)});
208                        case (0x0F):
209                            return MessageFormat.format(Bundle.getMessage("LOCO_CMD_Op1_0F"),
210                                    new Object[]{getLocoAddress(m), m.getElement(4)});
211                        case (0x10):
212                            return MessageFormat.format(Bundle.getMessage("LOCO_CMD_Op1_10"),
213                                    new Object[]{getLocoAddress(m), m.getElement(4)});
214                        case (0x11):
215                            return MessageFormat.format(Bundle.getMessage("LOCO_CMD_Op1_11"),
216                                    new Object[]{getLocoAddress(m), m.getElement(4)});
217                        case (0x12):
218                            return MessageFormat.format(Bundle.getMessage("LOCO_CMD_Op1_12"),
219                                    new Object[]{getLocoAddress(m), m.getElement(4)});
220                        case (0x15):
221                            return MessageFormat.format(Bundle.getMessage("LOCO_CMD_Op1_15"),
222                                    new Object[]{getLocoAddress(m), m.getElement(4)});
223                        case (0x16):
224                            return MessageFormat.format(Bundle.getMessage("LOCO_CMD_Op1_16"),
225                                    new Object[]{getLocoAddress(m), m.getElement(4)});
226                        case (0x17):
227                            return MessageFormat.format(Bundle.getMessage("LOCO_CMD_Op1_17"),
228                                    new Object[]{getLocoAddress(m), m.getElement(4)});
229                        default:
230                            log.error("Unhandled loco cmd op1 code: {}", m.getElement(3));
231                            break;
232                    }
233                }
234                break;
235            // Queue commands 0xA3 - 0xA5
236            case (NceMessage.QUEUEn_BYTES_CMD + 3):
237                if (m.getNumDataElements() == 5) {
238                    return MessageFormat.format(Bundle.getMessage("QUEUEn_BYTES_CMD"),
239                            new Object[]{"3", m.getElement(1), getDataBytes(m, 2, 3)});
240                }
241                break;
242            case (NceMessage.QUEUEn_BYTES_CMD + 4):
243                if (m.getNumDataElements() == 6) {
244                    return MessageFormat.format(Bundle.getMessage("QUEUEn_BYTES_CMD"),
245                            new Object[]{"4", m.getElement(1), getDataBytes(m, 2, 4)});
246                }
247                break;
248            case (NceMessage.QUEUEn_BYTES_CMD + 5):
249                if (m.getNumDataElements() == 7) {
250                    return MessageFormat.format(Bundle.getMessage("QUEUEn_BYTES_CMD"),
251                            new Object[]{"5", m.getElement(1), getDataBytes(m, 2, 5)});
252                }
253                break;
254            case (NceMessage.WRITE_REG_CMD):
255                if (m.getNumDataElements() == 3) {
256                    return MessageFormat.format(Bundle.getMessage("WRITE_REG_CMD"),
257                            new Object[]{m.getElement(1), getDataBytes(m, 2, 1)});
258                }
259                break;
260            case (NceMessage.WRITE_DIR_CV_CMD):
261                if (m.getNumDataElements() == 4) {
262                    return MessageFormat.format(Bundle.getMessage("WRITE_DIR_CV_CMD"),
263                            new Object[]{getNumber(m), getDataBytes(m, 3, 1)});
264                }
265                break;
266            case (NceMessage.SEND_ACC_SIG_MACRO_CMD):
267                if (m.getNumDataElements() == 5) {
268                    // byte three is the Op_1
269                    switch (m.getElement(3)) {
270                        case (1):
271                            return MessageFormat.format(Bundle.getMessage("ACC_CMD_Op1_01"),
272                                    new Object[]{m.getElement(4)});
273                        case (3):
274                            return MessageFormat.format(Bundle.getMessage("ACC_CMD_Op1_03"),
275                                    new Object[]{getNumber(m)});
276                        case (4):
277                            return MessageFormat.format(Bundle.getMessage("ACC_CMD_Op1_04"),
278                                    new Object[]{getNumber(m)});
279                        case (5):
280                            return MessageFormat.format(Bundle.getMessage("ACC_CMD_Op1_05"),
281                                    new Object[]{getNumber(m), m.getElement(4)});
282                        default:
283                            log.error("Unhandled acc cmd op1 code: {}", m.getElement(3));
284                            break;
285                    }
286                }
287                break;
288            case (NceMessage.USB_SET_CAB_CMD):
289                if (m.getNumDataElements() == 2) {
290                    return MessageFormat.format(Bundle.getMessage("Usb_Set_Cab_Op1"),
291                            new Object[]{m.getElement(1)});
292                }
293                break;
294            case (NceMessage.USB_MEM_POINTER_CMD):
295                if (m.getNumDataElements() == 3) {
296                    return MessageFormat.format(Bundle.getMessage("Usb_Set_Mem_Ptr_Cmd"),
297                            new Object[]{m.getElement(1), m.getElement(2)});
298                }
299                break;
300            case (NceMessage.USB_MEM_WRITE_CMD):
301                if (m.getNumDataElements() == 2) {
302                    return MessageFormat.format(Bundle.getMessage("Usb_Mem_Write_Cmd"),
303                            new Object[]{m.getElement(1)});
304                }
305                break;
306            default:
307//                log.debug("Unhandled command code: {} after 1st pass", Integer.toHexString(m.getOpCode() & 0xFF));
308                break;
309        }
310        // 2nd pass, check for messages that have a data reply
311        replyType = REPLY_DATA;
312        switch (m.getOpCode() & 0xFF) {
313            case (NceMessage.READ_CLOCK_CMD):
314                return Bundle.getMessage("READ_CLOCK_CMD");
315            case (NceMessage.READ_AUI4_CMD):
316                if (m.getNumDataElements() == 2) {
317                    return MessageFormat.format(Bundle.getMessage("READ_AUI4_CMD"),
318                            new Object[]{m.getElement(1)});
319                }
320                break;
321            case (NceMessage.DUMMY_CMD):
322                return Bundle.getMessage("DUMMY_CMD");
323            case (NceMessage.READ16_CMD):
324                if (m.getNumDataElements() == 3) {
325                    return MessageFormat.format(Bundle.getMessage("READ16_CMD"),
326                            new Object[]{getAddress(m)});
327                }
328                break;
329            case (NceMessage.USB_MEM_READ_CMD):
330                if (m.getNumDataElements() == 2) {
331                    return MessageFormat.format(Bundle.getMessage("Usb_Mem_Read_Cmd"),
332                            new Object[]{m.getElement(1)});
333                }
334                break;
335            case (NceMessage.READ_AUI2_CMD):
336                if (m.getNumDataElements() == 2) {
337                    return MessageFormat.format(Bundle.getMessage("READ_AUI2_CMD"),
338                            new Object[]{m.getElement(1)});
339                }
340                break;
341            case (NceMessage.READ1_CMD):
342                if (m.getNumDataElements() == 3) {
343                    return MessageFormat.format(Bundle.getMessage("READ1_CMD"),
344                            new Object[]{getAddress(m)});
345                }
346                break;
347            case (NceMessage.READ_PAGED_CV_CMD):
348                if (m.getNumDataElements() == 3) {
349                    return MessageFormat.format(Bundle.getMessage("READ_PAGED_CV_CMD"),
350                            new Object[]{getNumber(m)});
351                }
352                break;
353            case (NceMessage.READ_REG_CMD):
354                if (m.getNumDataElements() == 2) {
355                    return MessageFormat.format(Bundle.getMessage("READ_REG_CMD"),
356                            new Object[]{m.getElement(1)});
357                }
358                break;
359            case (NceMessage.READ_DIR_CV_CMD):
360                if (m.getNumDataElements() == 3) {
361                    return MessageFormat.format(Bundle.getMessage("READ_DIR_CV_CMD"),
362                            new Object[]{getNumber(m)});
363                }
364                break;
365            case (NceMessage.SW_REV_CMD):
366                return Bundle.getMessage("SW_REV_CMD");
367            default:
368                log.debug("Unhandled command code: {} after 2nd pass", Integer.toHexString(m.getOpCode() & 0xFF));
369                break;
370        }
371        // this is one we don't know about or haven't coded it up
372        replyType = REPLY_UNKNOWN;
373        log.debug("Unhandled command code: {}, display as raw", Integer.toHexString(m.getOpCode() & 0xFF));
374        return MessageFormat.format(Bundle.getMessage("BIN_CMD"), new Object[]{m.toString()});
375    }
376
377    private String getAddress(NceMessage m) {
378        return StringUtil.twoHexFromInt(m.getElement(1)) + StringUtil.twoHexFromInt(m.getElement(2));
379    }
380
381    private String getDataBytes(NceMessage m, int start, int number) {
382        StringBuilder sb = new StringBuilder(" ");
383        for (int i = start; i < start + number; i++) {
384            sb.append(StringUtil.twoHexFromInt(m.getElement(i))).append(" ");
385        }
386        return sb.toString();
387    }
388
389    private String getNumber(NceMessage m) {
390        return Integer.toString(((m.getElement(1) & 0xFF) << 8) | (m.getElement(2) & 0xFF));
391    }
392
393    private String getLocoAddress(NceMessage m) {
394        // show address type
395        String appendix = " (short)";
396        if ((m.getElement(1) & 0xE0) != 0) {
397            appendix = " (long)";
398        }
399        return Integer.toString(((m.getElement(1) & 0x3F) << 8) | (m.getElement(2) & 0xFF)) + appendix;
400    }
401
402    private String getFunctionNumber(NceMessage m) {
403        // byte three is the Op_1
404        switch (m.getElement(3)) {
405            case (7): {
406                StringBuilder buf = new StringBuilder();
407                if ((m.getElement(4) & 0x10) != 0) {
408                    buf.append(Bundle.getMessage("F0_ON")).append(", ");
409                } else {
410                    buf.append(Bundle.getMessage("F0_OFF")).append(", ");
411                }
412                if ((m.getElement(4) & 0x01) != 0) {
413                    buf.append(Bundle.getMessage("F1_ON")).append(", ");
414                } else {
415                    buf.append(Bundle.getMessage("F1_OFF")).append(", ");
416                }
417                if ((m.getElement(4) & 0x02) != 0) {
418                    buf.append(Bundle.getMessage("F2_ON")).append(", ");
419                } else {
420                    buf.append(Bundle.getMessage("F2_OFF")).append(", ");
421                }
422                if ((m.getElement(4) & 0x04) != 0) {
423                    buf.append(Bundle.getMessage("F3_ON")).append(", ");
424                } else {
425                    buf.append(Bundle.getMessage("F3_OFF")).append(", ");
426                }
427                if ((m.getElement(4) & 0x08) != 0) {
428                    buf.append(Bundle.getMessage("F4_ON"));
429                } else {
430                    buf.append(Bundle.getMessage("F4_OFF"));
431                }
432                return buf.toString();
433            }
434            case (8): {
435                StringBuilder buf = new StringBuilder();
436                if ((m.getElement(4) & 0x01) != 0) {
437                    buf.append(Bundle.getMessage("F5_ON")).append(", ");
438                } else {
439                    buf.append(Bundle.getMessage("F5_OFF")).append(", ");
440                }
441                if ((m.getElement(4) & 0x02) != 0) {
442                    buf.append(Bundle.getMessage("F6_ON")).append(", ");
443                } else {
444                    buf.append(Bundle.getMessage("F6_OFF")).append(", ");
445                }
446                if ((m.getElement(4) & 0x04) != 0) {
447                    buf.append(Bundle.getMessage("F7_ON")).append(", ");
448                } else {
449                    buf.append(Bundle.getMessage("F7_OFF")).append(", ");
450                }
451                if ((m.getElement(4) & 0x08) != 0) {
452                    buf.append(Bundle.getMessage("F8_ON"));
453                } else {
454                    buf.append(Bundle.getMessage("F8_OFF"));
455                }
456                return buf.toString();
457            }
458            case (9): {
459                StringBuilder buf = new StringBuilder();
460                if ((m.getElement(4) & 0x01) != 0) {
461                    buf.append(Bundle.getMessage("F9_ON")).append(", ");
462                } else {
463                    buf.append(Bundle.getMessage("F9_OFF")).append(", ");
464                }
465                if ((m.getElement(4) & 0x02) != 0) {
466                    buf.append(Bundle.getMessage("F10_ON")).append(", ");
467                } else {
468                    buf.append(Bundle.getMessage("F10_OFF")).append(", ");
469                }
470                if ((m.getElement(4) & 0x04) != 0) {
471                    buf.append(Bundle.getMessage("F11_ON")).append(", ");
472                } else {
473                    buf.append(Bundle.getMessage("F11_OFF")).append(", ");
474                }
475                if ((m.getElement(4) & 0x08) != 0) {
476                    buf.append(Bundle.getMessage("F12_ON"));
477                } else {
478                    buf.append(Bundle.getMessage("F12_OFF"));
479                }
480                return buf.toString();
481            }
482            default:
483                return ("Error");
484        }
485    }
486
487    /**
488     * Creates a reply message for the log, in a human-friendly form if
489     * possible.
490     *
491     * @param r the raw reply message
492     * @return the displayable message string
493     */
494    public String displayReply(NceReply r) {
495        return parseReply(r);
496    }
497
498    private String parseReply(NceReply r) {
499        switch (replyType) {
500            case (REPLY_STANDARD):
501                /* standard reply is a single byte
502                 * Errors returned:
503                 * '0'= command not supported
504                 * '1'= loco/accy/signal address out of range
505                 * '2'= cab address or op code out of range
506                 * '3'= CV address or data out of range
507                 * '4'= byte count out of range
508                 * '!'= command completed successfully
509                 */
510                if (r.getNumDataElements() == 1) {
511                    switch (r.getOpCode() & 0xFF) {
512                        case (REPLY_ZERO):
513                            return Bundle.getMessage("NceReplyZero");
514                        case (REPLY_ONE):
515                            return Bundle.getMessage("NceReplyOne");
516                        case (REPLY_TWO):
517                            return Bundle.getMessage("NceReplyTwo");
518                        case (REPLY_THREE):
519                            return Bundle.getMessage("NceReplyThree");
520                        case (REPLY_FOUR):
521                            return Bundle.getMessage("NceReplyFour");
522                        case (REPLY_OK):
523                            return Bundle.getMessage("NceReplyOK");
524                        default:
525                            log.error("Unhandled reply code: {}", Integer.toHexString(r.getOpCode() & 0xFF));
526                            break;
527                    }
528                }
529                break;
530            case (REPLY_ENTER_PROGRAMMING_MODE):
531                /* enter programming mode reply is a single byte
532                 * '3'= short circuit
533                 * '!'= command completed successfully
534                 */
535                if (r.getNumDataElements() == 1) {
536                    switch (r.getOpCode() & 0xFF) {
537                        case (REPLY_THREE):
538                            return Bundle.getMessage("NceReplyThreeProg");
539                        case (REPLY_OK):
540                            return Bundle.getMessage("NceReplyOK");
541                        default:
542                            log.error("Unhandled programming reply code: {}", Integer.toHexString(r.getOpCode() & 0xFF));
543                            break;
544                    }
545                }
546                break;
547            case (REPLY_DATA):
548                break;
549            default:
550                log.debug("Unhandled reply type code1: {}, display as raw", replyType);
551                break;
552        }
553        return MessageFormat.format(Bundle.getMessage("NceReply"), new Object[]{r.toString()});
554    }
555
556}