001package jmri.jmrix.lenz;
002
003import java.io.Serializable;
004
005import org.slf4j.Logger;
006import org.slf4j.LoggerFactory;
007import jmri.SpeedStepMode;
008
009/**
010 * Represents a single command or response on the XpressNet.
011 * <p>
012 * Content is represented with ints to avoid the problems with sign-extension
013 * that bytes have, and because a Java char is actually a variable number of
014 * bytes in Unicode.
015 *
016 * @author Bob Jacobsen Copyright (C) 2002
017 * @author Paul Bender Copyright (C) 2003-2010
018  *
019 */
020public class XNetMessage extends jmri.jmrix.AbstractMRMessage implements Serializable {
021
022    private static final String X_NET_MESSAGE_REQUEST_LI_BAUD = "XNetMessageRequestLIBaud";
023    private static final String X_NET_MESSAGE_REQUEST_SERVICE_MODE_READ_DIRECT_V_36 = "XNetMessageRequestServiceModeReadDirectV36";
024    private static final String X_NET_MESSAGE_REQUEST_SERVICE_MODE_WRITE_DIRECT_V_36 = "XNetMessageRequestServiceModeWriteDirectV36";
025    private static final String FORWARD = "Forward";
026    private static final String REVERSE = "Reverse";
027    private static final String X_NET_MESSAGE_SET_SPEED = "XNetMessageSetSpeed";
028    private static final String X_NET_MESSAGE_SET_DIRECTION = "XNetMessageSetDirection";
029    private static final String X_NET_MESSAGE_SET_FUNCTION_GROUP_X = "XNetMessageSetFunctionGroupX";
030    private static final String POWER_STATE_ON = "PowerStateOn";
031    private static final String POWER_STATE_OFF = "PowerStateOff";
032    private static final String SPEED_STEP_MODE_X = "SpeedStepModeX";
033    private static final String X_NET_MESSAGE_SET_FUNCTION_GROUP_X_MOMENTARY = "XNetMessageSetFunctionGroupXMomentary";
034    private static final String FUNCTION_CONTINUOUS = "FunctionContinuous";
035    private static final String FUNCTION_MOMENTARY = "FunctionMomentary";
036    private static int _nRetries = 5;
037
038    /* According to the specification, XpressNet has a maximum timing
039     interval of 500 milliseconds during normal communications */
040    protected static final int XNetProgrammingTimeout = 10000;
041    private static int XNetMessageTimeout = 5000;
042
043    /**
044     * Create a new object, representing a specific-length message.
045     *
046     * @param len Total bytes in message, including opcode and error-detection
047     *            byte.  Valid values are 0 to 15 (0x0 to 0xF).
048     */
049    public XNetMessage(int len) {
050        super(len);
051        if (len > 15 ) {  // only check upper bound. Lower bound checked in
052                          // super call.
053            log.error("Invalid length in ctor: {}", len);
054            throw new IllegalArgumentException("Invalid length in ctor: " + len);
055        }
056        setBinary(true);
057        setRetries(_nRetries);
058        setTimeout(XNetMessageTimeout);
059        _nDataChars = len;
060    }
061
062    /**
063     * Create a new object, that is a copy of an existing message.
064     *
065     * @param message an existing XpressNet message
066     */
067    public XNetMessage(XNetMessage message) {
068        super(message);
069        setBinary(true);
070        setRetries(_nRetries);
071        setTimeout(XNetMessageTimeout);
072    }
073
074    /**
075     * Create an XNetMessage from an XNetReply.
076     * @param message existing XNetReply.
077     */
078    public XNetMessage(XNetReply message) {
079        super(message.getNumDataElements());
080        setBinary(true);
081        setRetries(_nRetries);
082        setTimeout(XNetMessageTimeout);
083        for (int i = 0; i < message.getNumDataElements(); i++) {
084            setElement(i, message.getElement(i));
085        }
086    }
087
088    /**
089     * Create an XNetMessage from a String containing bytes.
090     * @param s string containing data bytes.
091     */
092    public XNetMessage(String s) {
093        setBinary(true);
094        setRetries(_nRetries);
095        setTimeout(XNetMessageTimeout);
096        // gather bytes in result
097        byte[] b = jmri.util.StringUtil.bytesFromHexString(s);
098        if (b.length == 0) {
099            // no such thing as a zero-length message
100            _nDataChars = 0;
101            _dataChars = null;
102            return;
103        }
104        _nDataChars = b.length;
105        _dataChars = new int[_nDataChars];
106        for (int i = 0; i < b.length; i++) {
107            setElement(i, b[i]);
108        }
109    }
110
111    // note that the opcode is part of the message, so we treat it
112    // directly
113    // WARNING: use this only with opcodes that have a variable number
114    // of arguments following included. Otherwise, just use setElement
115    @Override
116    public void setOpCode(int i) {
117        if (i > 0xF || i < 0) {
118            log.error("Opcode invalid: {}", i);
119        }
120        setElement(0, ((i * 16) & 0xF0) | ((getNumDataElements() - 2) & 0xF));
121    }
122
123    @Override
124    public int getOpCode() {
125        return (getElement(0) / 16) & 0xF;
126    }
127
128    /**
129     * Get a String representation of the op code in hex.
130     * {@inheritDoc}
131     */
132    @Override
133    public String getOpCodeHex() {
134        return "0x" + Integer.toHexString(getOpCode());
135    }
136
137    /**
138     * Check whether the message has a valid parity.
139     * @return true if parity valid, else false.
140     */
141    public boolean checkParity() {
142        int len = getNumDataElements();
143        int chksum = 0x00;  /* the seed */
144
145        int loop;
146
147        for (loop = 0; loop < len - 1; loop++) {  // calculate contents for data part
148            chksum ^= getElement(loop);
149        }
150        return ((chksum & 0xFF) == getElement(len - 1));
151    }
152
153    public void setParity() {
154        int len = getNumDataElements();
155        int chksum = 0x00;  /* the seed */
156
157        int loop;
158
159        for (loop = 0; loop < len - 1; loop++) {  // calculate contents for data part
160            chksum ^= getElement(loop);
161        }
162        setElement(len - 1, chksum & 0xFF);
163    }
164
165    /**
166     * Get an integer representation of a BCD value.
167     * @param n message element index.
168     * @return integer of BCD.
169     */
170    public Integer getElementBCD(int n) {
171        return Integer.decode(Integer.toHexString(getElement(n)));
172    }
173
174    /**
175     * Get the message length.
176     * @return message length.
177     */
178    public int length() {
179        return _nDataChars;
180    }
181
182    /**
183     * Set the default number of retries for an XpressNet message.
184     *
185     * @param t number of retries to attempt
186     */
187    public static void setXNetMessageRetries(int t) {
188        _nRetries = t;
189    }
190
191    /**
192     * Set the default timeout for an XpressNet message.
193     *
194     * @param t Timeout in milliseconds
195     */
196    public static void setXNetMessageTimeout(int t) {
197        XNetMessageTimeout = t;
198    }
199
200    /**
201     * Most messages are sent with a reply expected, but
202     * we have a few that we treat as though the reply is always
203     * a broadcast message, because the reply usually comes to us
204     * that way.
205     * {@inheritDoc}
206     */
207    @Override
208    public boolean replyExpected() {
209        return !broadcastReply;
210    }
211
212    private boolean broadcastReply = false;
213
214    /**
215     * Tell the traffic controller we expect this
216     * message to have a broadcast reply.
217     */
218    public void setBroadcastReply() {
219        broadcastReply = true;
220    }
221
222    // decode messages of a particular form
223    // create messages of a particular form
224
225    /**
226     * Encapsulate an NMRA DCC packet in an XpressNet message.
227     * <p>
228     * On Current (v3.5) Lenz command stations, the Operations Mode
229     *     Programming Request is implemented by sending a packet directly
230     *     to the rails.  This packet is not checked by the XpressNet
231     *     protocol, and is just the track packet with an added header
232     *     byte.
233     *     <p>
234     *     NOTE: Lenz does not say this will work for anything but 5
235     *     byte packets.
236     * @param packet byte array containing packet data elements.
237     * @return message to send DCC packet.
238     */
239    public static XNetMessage getNMRAXNetMsg(byte[] packet) {
240        XNetMessage msg = new XNetMessage(packet.length + 2);
241        msg.setOpCode((XNetConstants.OPS_MODE_PROG_REQ & 0xF0) >> 4);
242        msg.setElement(1, 0x30);
243        for (int i = 0; i < packet.length; i++) {
244            msg.setElement((i + 2), packet[i] & 0xff);
245        }
246        msg.setParity();
247        return (msg);
248    }
249
250    /*
251     * The next group of routines are used by Feedback and/or turnout
252     * control code.  These are used in multiple places within the code,
253     * so they appear here.
254     */
255
256    /**
257     * Generate a message to change turnout state.
258     * @param pNumber address number.
259     * @param pClose true if set turnout closed.
260     * @param pThrow true if set turnout thrown.
261     * @param pOn accessory line true for on, false off.
262     * @return message containing turnout command.
263     */
264    public static XNetMessage getTurnoutCommandMsg(int pNumber, boolean pClose,
265            boolean pThrow, boolean pOn) {
266        XNetMessage l = new XNetMessage(4);
267        l.setElement(0, XNetConstants.ACC_OPER_REQ);
268
269        // compute address byte fields
270        int hiadr = (pNumber - 1) / 4;
271        int loadr = ((pNumber - 1) - hiadr * 4) * 2;
272        // The MSB of the upper nibble is required to be set on
273        // The rest of the upper nibble should be zeros.
274        // The MSB of the lower nibble says weather or not the
275        // accessory line should be "on" or "off"
276        if (!pOn) {
277            loadr |= 0x80;
278        } else {
279            loadr |= 0x88;
280        }
281        // If we are sending a "throw" command, we set the LSB of the
282        // lower nibble on, otherwise, we leave it "off".
283        if (pThrow) {
284            loadr |= 0x01;
285        }
286
287        // we don't know how to command both states right now!
288        if (pClose && pThrow) {
289            log.error("XpressNet turnout logic can't handle both THROWN and CLOSED yet");
290        }
291        // store and send
292        l.setElement(1, hiadr);
293        l.setElement(2, loadr);
294        l.setParity(); // Set the parity bit
295
296        return l;
297    }
298
299    /**
300     * Generate a message to receive the feedback information for an upper or
301     * lower nibble of the feedback address in question.
302     * @param pNumber feedback address.
303     * @param pLowerNibble true for upper nibble, else false for lower.
304     * @return feedback request message.
305     */
306    public static XNetMessage getFeedbackRequestMsg(int pNumber,
307            boolean pLowerNibble) {
308        XNetMessage l = new XNetMessage(4);
309        l.setBroadcastReply();  // we the message reply as a broadcast message.
310        l.setElement(0, XNetConstants.ACC_INFO_REQ);
311
312        // compute address byte field
313        l.setElement(1, (pNumber - 1) / 4);
314        // The MSB of the upper nibble is required to be set on
315        // The rest of the upper nibble should be zeros.
316        // The LSB of the lower nibble says weather or not the
317        // information request is for the upper or lower nibble.
318        if (pLowerNibble) {
319            l.setElement(2, 0x80);
320        } else {
321            l.setElement(2, 0x81);
322        }
323        l.setParity(); // Set the parity bit
324        return l;
325    }
326
327    /*
328     * Next, we have some messages related to sending programming commands.
329     */
330
331    public static XNetMessage getServiceModeResultsMsg() {
332        XNetMessage m = new XNetMessage(3);
333        m.setNeededMode(jmri.jmrix.AbstractMRTrafficController.PROGRAMINGMODE);
334        m.setTimeout(XNetProgrammingTimeout);
335        m.setElement(0, XNetConstants.CS_REQUEST);
336        m.setElement(1, XNetConstants.SERVICE_MODE_CSRESULT);
337        m.setParity(); // Set the parity bit
338        return m;
339    }
340
341    public static XNetMessage getExitProgModeMsg() {
342        XNetMessage m = new XNetMessage(3);
343        m.setNeededMode(jmri.jmrix.AbstractMRTrafficController.PROGRAMINGMODE);
344        m.setElement(0, XNetConstants.CS_REQUEST);
345        m.setElement(1, XNetConstants.RESUME_OPS);
346        m.setParity();
347        return m;
348    }
349
350    public static XNetMessage getReadPagedCVMsg(int cv) {
351        XNetMessage m = new XNetMessage(4);
352        m.setNeededMode(jmri.jmrix.AbstractMRTrafficController.PROGRAMINGMODE);
353        m.setTimeout(XNetProgrammingTimeout);
354        m.setElement(0, XNetConstants.PROG_READ_REQUEST);
355        m.setElement(1, XNetConstants.PROG_READ_MODE_PAGED);
356        m.setElement(2, (0xff & cv));
357        m.setParity(); // Set the parity bit
358        return m;
359    }
360
361    public static XNetMessage getReadDirectCVMsg(int cv) {
362        XNetMessage m = new XNetMessage(4);
363        m.setNeededMode(jmri.jmrix.AbstractMRTrafficController.PROGRAMINGMODE);
364        m.setTimeout(XNetProgrammingTimeout);
365        m.setElement(0, XNetConstants.PROG_READ_REQUEST);
366        if (cv < 0x0100) /* Use the version 3.5 command for CVs <= 256 */ {
367            m.setElement(1, XNetConstants.PROG_READ_MODE_CV);
368        } else if (cv == 0x0400) /* For CV1024, we need to send the version 3.6
369         command for CVs 1 to 256, sending a 0 for the
370         CV */ {
371            m.setElement(1, XNetConstants.PROG_READ_MODE_CV_V36);
372        } else /* and the version 3.6 command for CVs > 256 */ {
373            m.setElement(1, XNetConstants.PROG_READ_MODE_CV_V36 | ((cv & 0x0300) >> 8));
374        }
375        m.setElement(2, (0xff & cv));
376        m.setParity(); // Set the parity bit
377        return m;
378    }
379
380    public static XNetMessage getWritePagedCVMsg(int cv, int val) {
381        XNetMessage m = new XNetMessage(5);
382        m.setNeededMode(jmri.jmrix.AbstractMRTrafficController.PROGRAMINGMODE);
383        m.setTimeout(XNetProgrammingTimeout);
384        m.setElement(0, XNetConstants.PROG_WRITE_REQUEST);
385        m.setElement(1, XNetConstants.PROG_WRITE_MODE_PAGED);
386        m.setElement(2, (0xff & cv));
387        m.setElement(3, val);
388        m.setParity(); // Set the parity bit
389        return m;
390    }
391
392    public static XNetMessage getWriteDirectCVMsg(int cv, int val) {
393        XNetMessage m = new XNetMessage(5);
394        m.setNeededMode(jmri.jmrix.AbstractMRTrafficController.PROGRAMINGMODE);
395        m.setTimeout(XNetProgrammingTimeout);
396        m.setElement(0, XNetConstants.PROG_WRITE_REQUEST);
397        if (cv < 0x0100) /* Use the version 3.5 command for CVs <= 256 */ {
398            m.setElement(1, XNetConstants.PROG_WRITE_MODE_CV);
399        } else if (cv == 0x0400) /* For CV1024, we need to send the version 3.6
400         command for CVs 1 to 256, sending a 0 for the
401         CV */ {
402            m.setElement(1, XNetConstants.PROG_WRITE_MODE_CV_V36);
403        } else /* and the version 3.6 command for CVs > 256 */ {
404            m.setElement(1, XNetConstants.PROG_WRITE_MODE_CV_V36 | ((cv & 0x0300) >> 8));
405        }
406        m.setElement(2, (0xff & cv));
407        m.setElement(3, val);
408        m.setParity(); // Set the parity bit
409        return m;
410    }
411
412    public static XNetMessage getReadRegisterMsg(int reg) {
413        if (reg > 8) {
414            log.error("register number too large: {}",reg);
415        }
416        XNetMessage m = new XNetMessage(4);
417        m.setNeededMode(jmri.jmrix.AbstractMRTrafficController.PROGRAMINGMODE);
418        m.setTimeout(XNetProgrammingTimeout);
419        m.setElement(0, XNetConstants.PROG_READ_REQUEST);
420        m.setElement(1, XNetConstants.PROG_READ_MODE_REGISTER);
421        m.setElement(2, (0x0f & reg));
422        m.setParity(); // Set the parity bit
423        return m;
424    }
425
426    public static XNetMessage getWriteRegisterMsg(int reg, int val) {
427        if (reg > 8) {
428            log.error("register number too large: {}",reg);
429        }
430        XNetMessage m = new XNetMessage(5);
431        m.setNeededMode(jmri.jmrix.AbstractMRTrafficController.PROGRAMINGMODE);
432        m.setTimeout(XNetProgrammingTimeout);
433        m.setElement(0, XNetConstants.PROG_WRITE_REQUEST);
434        m.setElement(1, XNetConstants.PROG_WRITE_MODE_REGISTER);
435        m.setElement(2, (0x0f & reg));
436        m.setElement(3, val);
437        m.setParity(); // Set the parity bit
438        return m;
439    }
440
441    public static XNetMessage getWriteOpsModeCVMsg(int AH, int AL, int cv, int val) {
442        XNetMessage m = new XNetMessage(8);
443        m.setElement(0, XNetConstants.OPS_MODE_PROG_REQ);
444        m.setElement(1, XNetConstants.OPS_MODE_PROG_WRITE_REQ);
445        m.setElement(2, AH);
446        m.setElement(3, AL);
447        /* Element 4 is 0xEC + the upper two  bits of the 10 bit CV address.
448         NOTE: This is the track packet CV, not the human readable CV, so
449         its value actually is one less than what we normally think of it as.*/
450        int temp = (cv - 1) & 0x0300;
451        temp = temp / 0x00FF;
452        m.setElement(4, 0xEC + temp);
453        /* Element 5 is the lower 8 bits of the cv */
454        m.setElement(5, ((0x00ff & cv) - 1));
455        m.setElement(6, val);
456        m.setParity(); // Set the parity bit
457        return m;
458    }
459
460    public static XNetMessage getVerifyOpsModeCVMsg(int AH, int AL, int cv, int val) {
461        XNetMessage m = new XNetMessage(8);
462        m.setElement(0, XNetConstants.OPS_MODE_PROG_REQ);
463        m.setElement(1, XNetConstants.OPS_MODE_PROG_WRITE_REQ);
464        m.setElement(2, AH);
465        m.setElement(3, AL);
466        /* Element 4 is 0xE4 + the upper two  bits of the 10 bit CV address.
467         NOTE: This is the track packet CV, not the human readable CV, so
468         its value actually is one less than what we normally think of it as.*/
469        int temp = (cv - 1) & 0x0300;
470        temp = temp / 0x00FF;
471        m.setElement(4, 0xE4 + temp);
472        /* Element 5 is the lower 8 bits of the cv */
473        m.setElement(5, ((0x00ff & cv) - 1));
474        m.setElement(6, val);
475        m.setParity(); // Set the parity bit
476        return m;
477    }
478
479    public static XNetMessage getBitWriteOpsModeCVMsg(int AH, int AL, int cv, int bit, boolean value) {
480        XNetMessage m = new XNetMessage(8);
481        m.setElement(0, XNetConstants.OPS_MODE_PROG_REQ);
482        m.setElement(1, XNetConstants.OPS_MODE_PROG_WRITE_REQ);
483        m.setElement(2, AH);
484        m.setElement(3, AL);
485        /* Element 4 is 0xE8 + the upper two  bits of the 10 bit CV address.
486         NOTE: This is the track packet CV, not the human readable CV, so
487         its value actually is one less than what we normally think of it as.*/
488        int temp = (cv - 1) & 0x0300;
489        temp = temp / 0x00FF;
490        m.setElement(4, 0xE8 + temp);
491        /* Element 5 is the lower 8 bits of the cv */
492        m.setElement(5, ((0x00ff & cv) - 1));
493        /* Since this is a bit write, Element 6 is:
494         0xE0 +
495         bit 3 is the value to write
496         bit's 0-2 are the location of the bit we are changing */
497        if (value) {
498            m.setElement(6, ((0xe8) | (bit & 0xff)));
499        } else // value == false
500        {
501            m.setElement(6, ((0xe0) | (bit & 0xff)));
502        }
503        m.setParity(); // Set the parity bit
504        return m;
505    }
506
507    public static XNetMessage getBitVerifyOpsModeCVMsg(int AH, int AL, int cv, int bit, boolean value) {
508        XNetMessage m = new XNetMessage(8);
509        m.setElement(0, XNetConstants.OPS_MODE_PROG_REQ);
510        m.setElement(1, XNetConstants.OPS_MODE_PROG_WRITE_REQ);
511        m.setElement(2, AH);
512        m.setElement(3, AL);
513        /* Element 4 is 0xE8 + the upper two  bits of the 10 bit CV address.
514         NOTE: This is the track packet CV, not the human readable CV, so
515         its value actually is one less than what we normally think of it as.*/
516        int temp = (cv - 1) & 0x0300;
517        temp = temp / 0x00FF;
518        m.setElement(4, 0xE8 + temp);
519        /* Element 5 is the lower 8 bits of the cv */
520        m.setElement(5, ((0x00ff & cv) - 1));
521        /* Since this is a bit verify, Element 6 is:
522         0xF0 +
523         bit 3 is the value to write
524         bit's 0-2 are the location of the bit we are changing */
525        if (value) {
526            m.setElement(6, ((0xf8) | (bit & 0xff)));
527        } else // value == false
528        {
529            m.setElement(6, ((0xf0) | (bit & 0xff)));
530        }
531        m.setParity(); // Set the parity bit
532        return m;
533    }
534
535    /*
536     * Next, we have routines to generate XpressNet Messages for building
537     * and tearing down a consist or a double header.
538     */
539
540    /**
541     * Build a Double Header.
542     *
543     * @param address1 the first address in the consist
544     * @param address2 the second address in the consist.
545     * @return message to build double header.
546     */
547    public static XNetMessage getBuildDoubleHeaderMsg(int address1, int address2) {
548        XNetMessage msg = new XNetMessage(7);
549        msg.setElement(0, XNetConstants.LOCO_DOUBLEHEAD);
550        msg.setElement(1, XNetConstants.LOCO_DOUBLEHEAD_BYTE2);
551        msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address1));
552        msg.setElement(3, LenzCommandStation.getDCCAddressLow(address1));
553        msg.setElement(4, LenzCommandStation.getDCCAddressHigh(address2));
554        msg.setElement(5, LenzCommandStation.getDCCAddressLow(address2));
555        msg.setParity();
556        return (msg);
557    }
558
559    /**
560     * Dissolve a Double Header.
561     *
562     * @param address one of the two addresses in the Double Header
563     * @return message to dissolve a double header.
564     */
565    public static XNetMessage getDisolveDoubleHeaderMsg(int address) {
566        // All we have to do is call getBuildDoubleHeaderMsg with the
567        // second address as a zero
568        return (getBuildDoubleHeaderMsg(address, 0));
569    }
570
571    /**
572     * Add a Single address to a specified Advanced consist.
573     *
574     * @param consist the consist address (1-99)
575     * @param address the locomotive address to add.
576     * @param isNormalDir tells us if the locomotive is going forward when
577     * the consist is going forward.
578     * @return message to add address to consist.
579     */
580    public static XNetMessage getAddLocoToConsistMsg(int consist, int address,
581            boolean isNormalDir) {
582        XNetMessage msg = new XNetMessage(6);
583        msg.setElement(0, XNetConstants.LOCO_OPER_REQ);
584        if (isNormalDir) {
585            msg.setElement(1, XNetConstants.LOCO_ADD_MULTI_UNIT_REQ);
586        } else {
587            msg.setElement(1, XNetConstants.LOCO_ADD_MULTI_UNIT_REQ | 0x01);
588        }
589        msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address));
590        msg.setElement(3, LenzCommandStation.getDCCAddressLow(address));
591        msg.setElement(4, consist);
592        msg.setParity();
593        return (msg);
594    }
595
596    /**
597     * Remove a Single address to a specified Advanced consist.
598     *
599     * @param consist the consist address (1-99)
600     * @param address the locomotive address to remove
601     * @return message to remove single address from consist.
602     */
603    public static XNetMessage getRemoveLocoFromConsistMsg(int consist, int address) {
604        XNetMessage msg = new XNetMessage(6);
605        msg.setElement(0, XNetConstants.LOCO_OPER_REQ);
606        msg.setElement(1, XNetConstants.LOCO_REM_MULTI_UNIT_REQ);
607        msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address));
608        msg.setElement(3, LenzCommandStation.getDCCAddressLow(address));
609        msg.setElement(4, consist);
610        msg.setParity();
611        return (msg);
612    }
613
614
615    /*
616     * Next, we have routines to generate XpressNet Messages for search
617     * and manipulation of the Command Station Database
618     */
619
620    /**
621     * Given a locomotive address, search the database for the next
622     * member.
623     * (if the Address is zero start at the beginning of the database).
624     *
625     * @param address is the locomotive address
626     * @param searchForward indicates to search the database Forward if
627     * true, or backwards if False
628     * @return message to request next address.
629     */
630    public static XNetMessage getNextAddressOnStackMsg(int address, boolean searchForward) {
631        XNetMessage msg = new XNetMessage(5);
632        msg.setElement(0, XNetConstants.LOCO_STATUS_REQ);
633        if (searchForward) {
634            msg.setElement(1, XNetConstants.LOCO_STACK_SEARCH_FWD);
635        } else {
636            msg.setElement(1, XNetConstants.LOCO_STACK_SEARCH_BKWD);
637        }
638        msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address));
639        msg.setElement(3, LenzCommandStation.getDCCAddressLow(address));
640        msg.setParity();
641        return (msg);
642    }
643
644    /**
645     * Given a consist address, search the database for the next Consist
646     * address.
647     *
648     * @param address is the consist address (in the range 1-99).
649     * If the Address is zero start at the beginning of the database.
650     * @param searchForward indicates to search the database Forward if
651     * true, or backwards if false
652     * @return message to get next consist address.
653     */
654    public static XNetMessage getDBSearchMsgConsistAddress(int address, boolean searchForward) {
655        XNetMessage msg = new XNetMessage(4);
656        msg.setElement(0, XNetConstants.CS_MULTI_UNIT_REQ);
657        if (searchForward) {
658            msg.setElement(1, XNetConstants.CS_MULTI_UNIT_REQ_FWD);
659        } else {
660            msg.setElement(1, XNetConstants.CS_MULTI_UNIT_REQ_BKWD);
661        }
662        msg.setElement(2, address);
663        msg.setParity();
664        return (msg);
665    }
666
667    /**
668     * Given a consist and a locomotive address, search the database for
669     * the next Locomotive in the consist.
670     *
671     * @param consist the consist address (1-99).
672     * If the Consist Address is zero start at the begining of the database
673     * @param address the locomotive address.
674     * If the Address is zero start at the begining of the consist
675     * @param searchForward indicates to search the database Forward if
676     * true, or backwards if False
677     * @return  message to request next loco in consist.
678     */
679    public static XNetMessage getDBSearchMsgNextMULoco(int consist, int address, boolean searchForward) {
680        XNetMessage msg = new XNetMessage(6);
681        msg.setElement(0, XNetConstants.LOCO_IN_MULTI_UNIT_SEARCH_REQ);
682        if (searchForward) {
683            msg.setElement(1, XNetConstants.LOCO_IN_MULTI_UNIT_REQ_FORWARD);
684        } else {
685            msg.setElement(1, XNetConstants.LOCO_IN_MULTI_UNIT_REQ_BACKWARD);
686        }
687        msg.setElement(2, consist);
688        msg.setElement(3, LenzCommandStation.getDCCAddressHigh(address));
689        msg.setElement(4, LenzCommandStation.getDCCAddressLow(address));
690        msg.setParity();
691        return (msg);
692    }
693
694    /**
695     * Given a locomotive address, delete it from the database .
696     *
697     * @param address the locomotive address
698     * @return message to delete loco address from stack.
699     */
700    public static XNetMessage getDeleteAddressOnStackMsg(int address) {
701        XNetMessage msg = new XNetMessage(5);
702        msg.setElement(0, XNetConstants.LOCO_STATUS_REQ);
703        msg.setElement(1, XNetConstants.LOCO_STACK_DELETE);
704        msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address));
705        msg.setElement(3, LenzCommandStation.getDCCAddressLow(address));
706        msg.setParity();
707        return (msg);
708    }
709
710    /**
711     * Given a locomotive address, request its status .
712     *
713     * @param address the locomotive address
714     * @return message to request loco status.
715     */
716    public static XNetMessage getLocomotiveInfoRequestMsg(int address) {
717        XNetMessage msg = new XNetMessage(5);
718        msg.setElement(0, XNetConstants.LOCO_STATUS_REQ);
719        msg.setElement(1, XNetConstants.LOCO_INFO_REQ_V3);
720        msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address));
721        msg.setElement(3, LenzCommandStation.getDCCAddressLow(address));
722        msg.setParity();
723        return (msg);
724    }
725
726    /**
727     * Given a locomotive address, request the function state (momentary status).
728     *
729     * @param address the locomotive address
730     * @return momentary function state request request.
731     */
732    public static XNetMessage getLocomotiveFunctionStatusMsg(int address) {
733        XNetMessage msg = new XNetMessage(5);
734        msg.setElement(0, XNetConstants.LOCO_STATUS_REQ);
735        msg.setElement(1, XNetConstants.LOCO_INFO_REQ_FUNC);
736        msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address));
737        msg.setElement(3, LenzCommandStation.getDCCAddressLow(address));
738        msg.setParity();
739        return (msg);
740    }
741
742    /**
743     * Given a locomotive address, request the function on/off state
744     * for functions 13-28
745     *
746     * @param address the locomotive address
747     * @return function state request request f13-f28.
748     */
749    public static XNetMessage getLocomotiveFunctionHighOnStatusMsg(int address) {
750        XNetMessage msg = new XNetMessage(5);
751        msg.setElement(0, XNetConstants.LOCO_STATUS_REQ);
752        msg.setElement(1, XNetConstants.LOCO_INFO_REQ_FUNC_HI_ON);
753        msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address));
754        msg.setElement(3, LenzCommandStation.getDCCAddressLow(address));
755        msg.setParity();
756        return (msg);
757    }
758
759    /**
760     * Given a locomotive address, request the function state (momentary status)
761     * for high functions (functions 13-28).
762     *
763     * @param address the locomotive address
764     * @return momentary function state request request f13-f28.
765     */
766    public static XNetMessage getLocomotiveFunctionHighMomStatusMsg(int address) {
767        XNetMessage msg = new XNetMessage(5);
768        msg.setElement(0, XNetConstants.LOCO_STATUS_REQ);
769        msg.setElement(1, XNetConstants.LOCO_INFO_REQ_FUNC_HI_MOM);
770        msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address));
771        msg.setElement(3, LenzCommandStation.getDCCAddressLow(address));
772        msg.setParity();
773        return (msg);
774    }
775
776    /*
777     * Generate an emergency stop for the specified address.
778     *
779     * @param address the locomotive address
780     */
781    public static XNetMessage getAddressedEmergencyStop(int address) {
782        XNetMessage msg = new XNetMessage(4);
783        msg.setElement(0, XNetConstants.EMERGENCY_STOP);
784        msg.setElement(1, LenzCommandStation.getDCCAddressHigh(address));
785        // set to the upper
786        // byte of the  DCC address
787        msg.setElement(2, LenzCommandStation.getDCCAddressLow(address));
788        // set to the lower byte
789        //of the DCC address
790        msg.setParity(); // Set the parity bit
791        return msg;
792    }
793
794    /**
795     * Generate a Speed and Direction Request message.
796     *
797     * @param address the locomotive address
798     * @param speedStepMode the speedstep mode see @jmri.DccThrottle
799     *                       for possible values.
800     * @param speed a normalized speed value (a floating point number between 0
801     *              and 1).  A negative value indicates emergency stop.
802     * @param isForward true for forward, false for reverse.
803     * @return set speed and direction message.
804     */
805    public static XNetMessage getSpeedAndDirectionMsg(int address,
806            SpeedStepMode speedStepMode,
807            float speed,
808            boolean isForward) {
809        XNetMessage msg = new XNetMessage(6);
810        msg.setElement(0, XNetConstants.LOCO_OPER_REQ);
811        int element4value = 0;   /* this is for holding the speed and
812         direction setting */
813
814        if (speedStepMode == SpeedStepMode.NMRA_DCC_128) {
815            // We're in 128 speed step mode
816            msg.setElement(1, XNetConstants.LOCO_SPEED_128);
817            // Now, we need to figure out what to send in element 4
818            // Remember, the speed steps are identified as 0-127 (in
819            // 128 step mode), not 1-128.
820            int speedVal = java.lang.Math.round(speed * 126);
821            // speed step 1 is reserved to indicate emergency stop,
822            // so we need to step over speed step 1
823            if (speedVal >= 1) {
824                element4value = speedVal + 1;
825            }
826        } else if (speedStepMode == SpeedStepMode.NMRA_DCC_28) {
827            // We're in 28 speed step mode
828            msg.setElement(1, XNetConstants.LOCO_SPEED_28);
829            // Now, we need to figure out what to send in element 4
830            int speedVal = java.lang.Math.round(speed * 28);
831            // The first speed step used is actually at 4 for 28
832            // speed step mode.
833            if (speedVal >= 1) {
834                speedVal += 3;
835            }
836            // We have to re-arange the bits, since bit 4 is the LSB,
837            // but other bits are in order from 0-3
838            element4value = ((speedVal & 0x1e) >> 1)
839                    + ((speedVal & 0x01) << 4);
840        } else if (speedStepMode == SpeedStepMode.NMRA_DCC_27) {
841            // We're in 27 speed step mode
842            msg.setElement(1, XNetConstants.LOCO_SPEED_27);
843            // Now, we need to figure out what to send in element 4
844            int speedVal = java.lang.Math.round(speed * 27);
845            // The first speed step used is actually at 4 for 27
846            // speed step mode.
847            if (speedVal >= 1) {
848                speedVal += 3;
849            }
850            // We have to re-arange the bits, since bit 4 is the LSB,
851            // but other bits are in order from 0-3
852            element4value = ((speedVal & 0x1e) >> 1)
853                    + ((speedVal & 0x01) << 4);
854        } else {
855            // We're in 14 speed step mode
856            msg.setElement(1, XNetConstants.LOCO_SPEED_14);
857            // Now, we need to figure out what to send in element 4
858            element4value = (int) (speed * 14);
859            int speedVal = java.lang.Math.round(speed * 14);
860            // The first speed step used is actually at 2 for 14
861            // speed step mode.
862            if (speedVal >= 1) {
863                element4value += 1;
864            }
865        }
866        msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address));
867        // set to the upper byte of the  DCC address
868        msg.setElement(3, LenzCommandStation.getDCCAddressLow(address));
869        // set to the lower byte
870        //of the DCC address
871        if (isForward) {
872            /* the direction bit is always the most significant bit */
873            element4value += 128;
874        }
875        msg.setElement(4, element4value);
876        msg.setParity(); // Set the parity bit
877        return msg;
878    }
879
880    /**
881     * Generate a Function Group One Operation Request message.
882     *
883     * @param address the locomotive address
884     * @param f0 is true if f0 is on, false if f0 is off
885     * @param f1 is true if f1 is on, false if f1 is off
886     * @param f2 is true if f2 is on, false if f2 is off
887     * @param f3 is true if f3 is on, false if f3 is off
888     * @param f4 is true if f4 is on, false if f4 is off
889     * @return set function group 1 message.
890     */
891    public static XNetMessage getFunctionGroup1OpsMsg(int address,
892            boolean f0,
893            boolean f1,
894            boolean f2,
895            boolean f3,
896            boolean f4) {
897        XNetMessage msg = new XNetMessage(6);
898        msg.setElement(0, XNetConstants.LOCO_OPER_REQ);
899        msg.setElement(1, XNetConstants.LOCO_SET_FUNC_GROUP1);
900        msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address));
901        // set to the upper byte of the  DCC address
902        msg.setElement(3, LenzCommandStation.getDCCAddressLow(address));
903        // set to the lower byte of the DCC address
904        // Now, we need to figure out what to send in element 3
905        int element4value = 0;
906        if (f0) {
907            element4value += 16;
908        }
909        if (f1) {
910            element4value += 1;
911        }
912        if (f2) {
913            element4value += 2;
914        }
915        if (f3) {
916            element4value += 4;
917        }
918        if (f4) {
919            element4value += 8;
920        }
921        msg.setElement(4, element4value);
922        msg.setParity(); // Set the parity bit
923        return msg;
924    }
925
926    /**
927     * Generate a Function Group One Set Momentary Functions message.
928     *
929     * @param address the locomotive address
930     * @param f0 is true if f0 is momentary
931     * @param f1 is true if f1 is momentary
932     * @param f2 is true if f2 is momentary
933     * @param f3 is true if f3 is momentary
934     * @param f4 is true if f4 is momentary
935     * @return set momentary function group 1 message.
936     */
937    public static XNetMessage getFunctionGroup1SetMomMsg(int address,
938            boolean f0,
939            boolean f1,
940            boolean f2,
941            boolean f3,
942            boolean f4) {
943        XNetMessage msg = new XNetMessage(6);
944        msg.setElement(0, XNetConstants.LOCO_OPER_REQ);
945        msg.setElement(1, XNetConstants.LOCO_SET_FUNC_GROUP1_MOMENTARY);
946        msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address));
947        // set to the upper byte of the  DCC address
948        msg.setElement(3, LenzCommandStation.getDCCAddressLow(address));
949        // set to the lower byte of the DCC address
950        // Now, we need to figure out what to send in element 3
951        int element4value = 0;
952        if (f0) {
953            element4value += 16;
954        }
955        if (f1) {
956            element4value += 1;
957        }
958        if (f2) {
959            element4value += 2;
960        }
961        if (f3) {
962            element4value += 4;
963        }
964        if (f4) {
965            element4value += 8;
966        }
967        msg.setElement(4, element4value);
968        msg.setParity(); // Set the parity bit
969        return msg;
970    }
971
972    /**
973     * Generate a Function Group Two Operation Request message.
974     *
975     * @param address the locomotive address
976     * @param f5 is true if f5 is on, false if f5 is off
977     * @param f6 is true if f6 is on, false if f6 is off
978     * @param f7 is true if f7 is on, false if f7 is off
979     * @param f8 is true if f8 is on, false if f8 is off
980     * @return set function group 2 message.
981     */
982    public static XNetMessage getFunctionGroup2OpsMsg(int address,
983            boolean f5,
984            boolean f6,
985            boolean f7,
986            boolean f8) {
987        XNetMessage msg = new XNetMessage(6);
988        msg.setElement(0, XNetConstants.LOCO_OPER_REQ);
989        msg.setElement(1, XNetConstants.LOCO_SET_FUNC_GROUP2);
990        msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address));
991        // set to the upper byte of the DCC address
992        msg.setElement(3, LenzCommandStation.getDCCAddressLow(address));
993        // set to the lower byte of the DCC address
994        // Now, we need to figure out what to send in element 3
995        int element4value = 0;
996        if (f5) {
997            element4value += 1;
998        }
999        if (f6) {
1000            element4value += 2;
1001        }
1002        if (f7) {
1003            element4value += 4;
1004        }
1005        if (f8) {
1006            element4value += 8;
1007        }
1008        msg.setElement(4, element4value);
1009        msg.setParity(); // Set the parity bit
1010        return msg;
1011    }
1012
1013    /**
1014     * Generate a Function Group Two Set Momentary Functions message.
1015     *
1016     * @param address the locomotive address
1017     * @param f5 is true if f5 is momentary
1018     * @param f6 is true if f6 is momentary
1019     * @param f7 is true if f7 is momentary
1020     * @param f8 is true if f8 is momentary
1021     * @return set momentary function group 2 message.
1022     */
1023    public static XNetMessage getFunctionGroup2SetMomMsg(int address,
1024            boolean f5,
1025            boolean f6,
1026            boolean f7,
1027            boolean f8) {
1028        XNetMessage msg = new XNetMessage(6);
1029        msg.setElement(0, XNetConstants.LOCO_OPER_REQ);
1030        msg.setElement(1, XNetConstants.LOCO_SET_FUNC_GROUP2_MOMENTARY);
1031        msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address));
1032        // set to the upper byte of the  DCC address
1033        msg.setElement(3, LenzCommandStation.getDCCAddressLow(address));
1034        // set to the lower byte of the DCC address
1035        // Now, we need to figure out what to send in element 3
1036        int element4value = 0;
1037        if (f5) {
1038            element4value += 1;
1039        }
1040        if (f6) {
1041            element4value += 2;
1042        }
1043        if (f7) {
1044            element4value += 4;
1045        }
1046        if (f8) {
1047            element4value += 8;
1048        }
1049        msg.setElement(4, element4value);
1050        msg.setParity(); // Set the parity bit
1051        return msg;
1052    }
1053
1054    /**
1055     * Generate a Function Group Three Operation Request message.
1056     *
1057     * @param address the locomotive address
1058     * @param f9 is true if f9 is on, false if f9 is off
1059     * @param f10 is true if f10 is on, false if f10 is off
1060     * @param f11 is true if f11 is on, false if f11 is off
1061     * @param f12 is true if f12 is on, false if f12 is off
1062     * @return set function group 3 message.
1063     */
1064    public static XNetMessage getFunctionGroup3OpsMsg(int address,
1065            boolean f9,
1066            boolean f10,
1067            boolean f11,
1068            boolean f12) {
1069        XNetMessage msg = new XNetMessage(6);
1070        msg.setElement(0, XNetConstants.LOCO_OPER_REQ);
1071        msg.setElement(1, XNetConstants.LOCO_SET_FUNC_GROUP3);
1072        msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address));
1073        // set to the upper byte of the  DCC address
1074        msg.setElement(3, LenzCommandStation.getDCCAddressLow(address));
1075        // set to the lower byte of the DCC address
1076        // Now, we need to figure out what to send in element 3
1077        int element4value = 0;
1078        if (f9) {
1079            element4value += 1;
1080        }
1081        if (f10) {
1082            element4value += 2;
1083        }
1084        if (f11) {
1085            element4value += 4;
1086        }
1087        if (f12) {
1088            element4value += 8;
1089        }
1090        msg.setElement(4, element4value);
1091        msg.setParity(); // Set the parity bit
1092        return msg;
1093    }
1094
1095    /**
1096     * Generate a Function Group Three Set Momentary Functions message.
1097     *
1098     * @param address the locomotive address
1099     * @param f9 is true if f9 is momentary
1100     * @param f10 is true if f10 is momentary
1101     * @param f11 is true if f11 is momentary
1102     * @param f12 is true if f12 is momentary
1103     * @return set momentary function group 3 message.
1104     */
1105    public static XNetMessage getFunctionGroup3SetMomMsg(int address,
1106            boolean f9,
1107            boolean f10,
1108            boolean f11,
1109            boolean f12) {
1110        XNetMessage msg = new XNetMessage(6);
1111        msg.setElement(0, XNetConstants.LOCO_OPER_REQ);
1112        msg.setElement(1, XNetConstants.LOCO_SET_FUNC_GROUP3_MOMENTARY);
1113        msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address));
1114        // set to the upper byte of the  DCC address
1115        msg.setElement(3, LenzCommandStation.getDCCAddressLow(address));
1116        // set to the lower byte of the DCC address
1117        // Now, we need to figure out what to send in element 3
1118        int element4value = 0;
1119        if (f9) {
1120            element4value += 1;
1121        }
1122        if (f10) {
1123            element4value += 2;
1124        }
1125        if (f11) {
1126            element4value += 4;
1127        }
1128        if (f12) {
1129            element4value += 8;
1130        }
1131        msg.setElement(4, element4value);
1132        msg.setParity(); // Set the parity bit
1133        return msg;
1134    }
1135
1136    /**
1137     * Generate a Function Group Four Operation Request message.
1138     *
1139     * @param address the locomotive address
1140     * @param f13 is true if f13 is on, false if f13 is off
1141     * @param f14 is true if f14 is on, false if f14 is off
1142     * @param f15 is true if f15 is on, false if f15 is off
1143     * @param f16 is true if f18 is on, false if f16 is off
1144     * @param f17 is true if f17 is on, false if f17 is off
1145     * @param f18 is true if f18 is on, false if f18 is off
1146     * @param f19 is true if f19 is on, false if f19 is off
1147     * @param f20 is true if f20 is on, false if f20 is off
1148     * @return set function group 4 message.
1149     */
1150    public static XNetMessage getFunctionGroup4OpsMsg(int address,
1151            boolean f13,
1152            boolean f14,
1153            boolean f15,
1154            boolean f16,
1155            boolean f17,
1156            boolean f18,
1157            boolean f19,
1158            boolean f20) {
1159        XNetMessage msg = new XNetMessage(6);
1160        msg.setElement(0, XNetConstants.LOCO_OPER_REQ);
1161        msg.setElement(1, XNetConstants.LOCO_SET_FUNC_GROUP4);
1162        msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address));
1163        // set to the upper byte of the  DCC address
1164        msg.setElement(3, LenzCommandStation.getDCCAddressLow(address));
1165        // set to the lower byte of the DCC address
1166        // Now, we need to figure out what to send in element 3
1167        int element4value = 0;
1168        if (f13) {
1169            element4value += 1;
1170        }
1171        if (f14) {
1172            element4value += 2;
1173        }
1174        if (f15) {
1175            element4value += 4;
1176        }
1177        if (f16) {
1178            element4value += 8;
1179        }
1180        if (f17) {
1181            element4value += 16;
1182        }
1183        if (f18) {
1184            element4value += 32;
1185        }
1186        if (f19) {
1187            element4value += 64;
1188        }
1189        if (f20) {
1190            element4value += 128;
1191        }
1192        msg.setElement(4, element4value);
1193        msg.setParity(); // Set the parity bit
1194        return msg;
1195    }
1196
1197    /**
1198     * Generate a Function Group Four Set Momentary Function message.
1199     *
1200     * @param address the locomotive address
1201     * @param f13 is true if f13 is Momentary
1202     * @param f14 is true if f14 is Momentary
1203     * @param f15 is true if f15 is Momentary
1204     * @param f16 is true if f18 is Momentary
1205     * @param f17 is true if f17 is Momentary
1206     * @param f18 is true if f18 is Momentary
1207     * @param f19 is true if f19 is Momentary
1208     * @param f20 is true if f20 is Momentary
1209     * @return set momentary function group 4 message.
1210     */
1211    public static XNetMessage getFunctionGroup4SetMomMsg(int address,
1212            boolean f13,
1213            boolean f14,
1214            boolean f15,
1215            boolean f16,
1216            boolean f17,
1217            boolean f18,
1218            boolean f19,
1219            boolean f20) {
1220        XNetMessage msg = new XNetMessage(6);
1221        msg.setElement(0, XNetConstants.LOCO_OPER_REQ);
1222        msg.setElement(1, XNetConstants.LOCO_SET_FUNC_GROUP4_MOMENTARY);
1223        msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address));
1224        // set to the upper byte of the  DCC address
1225        msg.setElement(3, LenzCommandStation.getDCCAddressLow(address));
1226        // set to the lower byte of the DCC address
1227        // Now, we need to figure out what to send in element 3
1228        int element4value = 0;
1229        if (f13) {
1230            element4value += 1;
1231        }
1232        if (f14) {
1233            element4value += 2;
1234        }
1235        if (f15) {
1236            element4value += 4;
1237        }
1238        if (f16) {
1239            element4value += 8;
1240        }
1241        if (f17) {
1242            element4value += 16;
1243        }
1244        if (f18) {
1245            element4value += 32;
1246        }
1247        if (f19) {
1248            element4value += 64;
1249        }
1250        if (f20) {
1251            element4value += 128;
1252        }
1253        msg.setElement(4, element4value);
1254        msg.setParity(); // Set the parity bit
1255        return msg;
1256    }
1257
1258    /**
1259     * Generate a Function Group Five Operation Request message.
1260     *
1261     * @param address the locomotive address
1262     * @param f21 is true if f21 is on, false if f21 is off
1263     * @param f22 is true if f22 is on, false if f22 is off
1264     * @param f23 is true if f23 is on, false if f23 is off
1265     * @param f24 is true if f24 is on, false if f24 is off
1266     * @param f25 is true if f25 is on, false if f25 is off
1267     * @param f26 is true if f26 is on, false if f26 is off
1268     * @param f27 is true if f27 is on, false if f27 is off
1269     * @param f28 is true if f28 is on, false if f28 is off
1270     * @return set function group 5 message.
1271     */
1272    public static XNetMessage getFunctionGroup5OpsMsg(int address,
1273            boolean f21,
1274            boolean f22,
1275            boolean f23,
1276            boolean f24,
1277            boolean f25,
1278            boolean f26,
1279            boolean f27,
1280            boolean f28) {
1281        XNetMessage msg = new XNetMessage(6);
1282        msg.setElement(0, XNetConstants.LOCO_OPER_REQ);
1283        msg.setElement(1, XNetConstants.LOCO_SET_FUNC_GROUP5);
1284        msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address));
1285        // set to the upper byte of the  DCC address
1286        msg.setElement(3, LenzCommandStation.getDCCAddressLow(address));
1287        // set to the lower byte of the DCC address
1288        // Now, we need to figure out what to send in element 3
1289        int element4value = 0;
1290        if (f21) {
1291            element4value += 1;
1292        }
1293        if (f22) {
1294            element4value += 2;
1295        }
1296        if (f23) {
1297            element4value += 4;
1298        }
1299        if (f24) {
1300            element4value += 8;
1301        }
1302        if (f25) {
1303            element4value += 16;
1304        }
1305        if (f26) {
1306            element4value += 32;
1307        }
1308        if (f27) {
1309            element4value += 64;
1310        }
1311        if (f28) {
1312            element4value += 128;
1313        }
1314        msg.setElement(4, element4value);
1315        msg.setParity(); // Set the parity bit
1316        return msg;
1317    }
1318
1319    /**
1320     * Generate a Function Group Five Set Momentary Function message.
1321     *
1322     * @param address the locomotive address
1323     * @param f21 is true if f21 is momentary
1324     * @param f22 is true if f22 is momentary
1325     * @param f23 is true if f23 is momentary
1326     * @param f24 is true if f24 is momentary
1327     * @param f25 is true if f25 is momentary
1328     * @param f26 is true if f26 is momentary
1329     * @param f27 is true if f27 is momentary
1330     * @param f28 is true if f28 is momentary
1331     * @return set momentary function group 5 message.
1332     */
1333    public static XNetMessage getFunctionGroup5SetMomMsg(int address,
1334            boolean f21,
1335            boolean f22,
1336            boolean f23,
1337            boolean f24,
1338            boolean f25,
1339            boolean f26,
1340            boolean f27,
1341            boolean f28) {
1342        XNetMessage msg = new XNetMessage(6);
1343        msg.setElement(0, XNetConstants.LOCO_OPER_REQ);
1344        msg.setElement(1, XNetConstants.LOCO_SET_FUNC_GROUP5_MOMENTARY);
1345        msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address));
1346        // set to the upper byte of the  DCC address
1347        msg.setElement(3, LenzCommandStation.getDCCAddressLow(address));
1348        // set to the lower byte of the DCC address
1349        // Now, we need to figure out what to send in element 3
1350        int element4value = 0;
1351        if (f21) {
1352            element4value += 1;
1353        }
1354        if (f22) {
1355            element4value += 2;
1356        }
1357        if (f23) {
1358            element4value += 4;
1359        }
1360        if (f24) {
1361            element4value += 8;
1362        }
1363        if (f25) {
1364            element4value += 16;
1365        }
1366        if (f26) {
1367            element4value += 32;
1368        }
1369        if (f27) {
1370            element4value += 64;
1371        }
1372        if (f28) {
1373            element4value += 128;
1374        }
1375        msg.setElement(4, element4value);
1376        msg.setParity(); // Set the parity bit
1377        return msg;
1378    }
1379
1380    // Generate a Function Group Operation Request message for some specific case.
1381    private static XNetMessage getFunctionGroupNOpsMsg(int address, int byte1,
1382            boolean fA,
1383            boolean fB,
1384            boolean fC,
1385            boolean fD,
1386            boolean fE,
1387            boolean fF,
1388            boolean fG,
1389            boolean fH) {
1390        XNetMessage msg = new XNetMessage(6);
1391        msg.setElement(0, XNetConstants.LOCO_OPER_REQ);
1392        msg.setElement(1, byte1);
1393        msg.setElement(2, LenzCommandStation.getDCCAddressHigh(address));
1394        // set to the upper byte of the  DCC address
1395        msg.setElement(3, LenzCommandStation.getDCCAddressLow(address));
1396        // set to the lower byte of the DCC address
1397        // Now, we need to figure out what to send in element 3
1398        int element4value = 0;
1399        if (fA) {
1400            element4value += 1;
1401        }
1402        if (fB) {
1403            element4value += 2;
1404        }
1405        if (fC) {
1406            element4value += 4;
1407        }
1408        if (fD) {
1409            element4value += 8;
1410        }
1411        if (fE) {
1412            element4value += 16;
1413        }
1414        if (fF) {
1415            element4value += 32;
1416        }
1417        if (fG) {
1418            element4value += 64;
1419        }
1420        if (fH) {
1421            element4value += 128;
1422        }
1423        msg.setElement(4, element4value);
1424        msg.setParity(); // Set the parity bit
1425        return msg;
1426    }
1427
1428    public static XNetMessage getFunctionGroup6OpsMsg(int address,
1429            boolean fA, boolean fB, boolean fC, boolean fD,
1430            boolean fE, boolean fF, boolean fG, boolean fH) {
1431        return getFunctionGroupNOpsMsg(address, XNetConstants.LOCO_SET_FUNC_GROUP6,
1432            fA, fB, fC, fD, fE, fF, fG, fH);
1433    }
1434
1435    public static XNetMessage getFunctionGroup7OpsMsg(int address,
1436            boolean fA, boolean fB, boolean fC, boolean fD,
1437            boolean fE, boolean fF, boolean fG, boolean fH) {
1438        return getFunctionGroupNOpsMsg(address, XNetConstants.LOCO_SET_FUNC_GROUP7,
1439            fA, fB, fC, fD, fE, fF, fG, fH);
1440    }
1441
1442    public static XNetMessage getFunctionGroup8OpsMsg(int address,
1443            boolean fA, boolean fB, boolean fC, boolean fD,
1444            boolean fE, boolean fF, boolean fG, boolean fH) {
1445        return getFunctionGroupNOpsMsg(address, XNetConstants.LOCO_SET_FUNC_GROUP8,
1446            fA, fB, fC, fD, fE, fF, fG, fH);
1447    }
1448
1449    public static XNetMessage getFunctionGroup9OpsMsg(int address,
1450            boolean fA, boolean fB, boolean fC, boolean fD,
1451            boolean fE, boolean fF, boolean fG, boolean fH) {
1452        return getFunctionGroupNOpsMsg(address, XNetConstants.LOCO_SET_FUNC_GROUP9,
1453            fA, fB, fC, fD, fE, fF, fG, fH);
1454    }
1455
1456    public static XNetMessage getFunctionGroup10OpsMsg(int address,
1457            boolean fA, boolean fB, boolean fC, boolean fD,
1458            boolean fE, boolean fF, boolean fG, boolean fH) {
1459        return getFunctionGroupNOpsMsg(address, XNetConstants.LOCO_SET_FUNC_GROUP10,
1460            fA, fB, fC, fD, fE, fF, fG, fH);
1461    }
1462
1463    public static XNetMessage getFunctionGroup6SetMomMsg(int address,
1464            boolean fA, boolean fB, boolean fC, boolean fD,
1465            boolean fE, boolean fF, boolean fG, boolean fH) {
1466        return getFunctionGroupNOpsMsg(address, XNetConstants.LOCO_SET_FUNC_GROUP6_MOMENTARY,
1467            fA, fB, fC, fD, fE, fF, fG, fH);
1468    }
1469
1470    public static XNetMessage getFunctionGroup7SetMomMsg(int address,
1471            boolean fA, boolean fB, boolean fC, boolean fD,
1472            boolean fE, boolean fF, boolean fG, boolean fH) {
1473        return getFunctionGroupNOpsMsg(address, XNetConstants.LOCO_SET_FUNC_GROUP7_MOMENTARY,
1474            fA, fB, fC, fD, fE, fF, fG, fH);
1475    }
1476
1477    public static XNetMessage getFunctionGroup8SetMomMsg(int address,
1478            boolean fA, boolean fB, boolean fC, boolean fD,
1479            boolean fE, boolean fF, boolean fG, boolean fH) {
1480        return getFunctionGroupNOpsMsg(address, XNetConstants.LOCO_SET_FUNC_GROUP8_MOMENTARY,
1481            fA, fB, fC, fD, fE, fF, fG, fH);
1482    }
1483
1484    public static XNetMessage getFunctionGroup9SetMomMsg(int address,
1485            boolean fA, boolean fB, boolean fC, boolean fD,
1486            boolean fE, boolean fF, boolean fG, boolean fH) {
1487        return getFunctionGroupNOpsMsg(address, XNetConstants.LOCO_SET_FUNC_GROUP9_MOMENTARY,
1488            fA, fB, fC, fD, fE, fF, fG, fH);
1489    }
1490
1491    public static XNetMessage getFunctionGroup10SetMomMsg(int address,
1492            boolean fA, boolean fB, boolean fC, boolean fD,
1493            boolean fE, boolean fF, boolean fG, boolean fH) {
1494        return getFunctionGroupNOpsMsg(address, XNetConstants.LOCO_SET_FUNC_GROUP10_MOMENTARY,
1495            fA, fB, fC, fD, fE, fF, fG, fH);
1496    }
1497
1498    /**
1499     * Build a Resume operations Message.
1500     * @return resume message.
1501     */
1502    public static XNetMessage getResumeOperationsMsg() {
1503        XNetMessage msg = new XNetMessage(3);
1504        msg.setElement(0, XNetConstants.CS_REQUEST);
1505        msg.setElement(1, XNetConstants.RESUME_OPS);
1506        msg.setParity();
1507        return (msg);
1508    }
1509
1510    /**
1511     * Build an EmergencyOff Message.
1512     * @return emergency off message.
1513     */
1514    public static XNetMessage getEmergencyOffMsg() {
1515        XNetMessage msg = new XNetMessage(3);
1516        msg.setElement(0, XNetConstants.CS_REQUEST);
1517        msg.setElement(1, XNetConstants.EMERGENCY_OFF);
1518        msg.setParity();
1519        return (msg);
1520    }
1521
1522    /**
1523     * Build an EmergencyStop Message.
1524     * @return emergency stop message.
1525     */
1526    public static XNetMessage getEmergencyStopMsg() {
1527        XNetMessage msg = new XNetMessage(2);
1528        msg.setElement(0, XNetConstants.ALL_ESTOP);
1529        msg.setParity();
1530        return (msg);
1531    }
1532
1533    /**
1534     * Generate the message to request the Command Station Hardware/Software
1535     * Version.
1536     * @return message to request CS hardware and software version.
1537     */
1538    public static XNetMessage getCSVersionRequestMessage() {
1539        XNetMessage msg = new XNetMessage(3);
1540        msg.setElement(0, XNetConstants.CS_REQUEST);
1541        msg.setElement(1, XNetConstants.CS_VERSION);
1542        msg.setParity(); // Set the parity bit
1543        return msg;
1544    }
1545
1546    /**
1547     * Generate the message to request the Command Station Status.
1548     * @return message to request CS status.
1549     */
1550    public static XNetMessage getCSStatusRequestMessage() {
1551        XNetMessage msg = new XNetMessage(3);
1552        msg.setElement(0, XNetConstants.CS_REQUEST);
1553        msg.setElement(1, XNetConstants.CS_STATUS);
1554        msg.setParity(); // Set the parity bit
1555        return msg;
1556    }
1557
1558    /**
1559     * Generate the message to set the Command Station to Auto or Manual restart
1560     * mode.
1561     * @param autoMode true if auto, false for manual.
1562     * @return message to set CS restart mode.
1563     */
1564    public static XNetMessage getCSAutoStartMessage(boolean autoMode) {
1565        XNetMessage msg = new XNetMessage(4);
1566        msg.setElement(0, XNetConstants.CS_SET_POWERMODE);
1567        msg.setElement(1, XNetConstants.CS_SET_POWERMODE);
1568        if (autoMode) {
1569            msg.setElement(2, XNetConstants.CS_POWERMODE_AUTO);
1570        } else {
1571            msg.setElement(2, XNetConstants.CS_POWERMODE_MANUAL);
1572        }
1573        msg.setParity(); // Set the parity bit
1574        return msg;
1575    }
1576
1577    /**
1578     * Generate the message to request the Computer Interface Hardware/Software
1579     * Version.
1580     * @return message to request interface hardware and software version.
1581     */
1582    public static XNetMessage getLIVersionRequestMessage() {
1583        XNetMessage msg = new XNetMessage(2);
1584        msg.setElement(0, XNetConstants.LI_VERSION_REQUEST);
1585        msg.setParity(); // Set the parity bit
1586        return msg;
1587    }
1588
1589    /**
1590     * Generate the message to set or request the Computer Interface Address.
1591     *
1592     * @param address Interface address (0-31). Send invalid address to request
1593     *                the address (32-255).
1594     * @return message to set or request interface address.
1595     */
1596    public static XNetMessage getLIAddressRequestMsg(int address) {
1597        XNetMessage msg = new XNetMessage(4);
1598        msg.setElement(0, XNetConstants.LI101_REQUEST);
1599        msg.setElement(1, XNetConstants.LI101_REQUEST_ADDRESS);
1600        msg.setElement(2, address);
1601        msg.setParity(); // Set the parity bit
1602        return msg;
1603    }
1604
1605    /**
1606     * Generate the message to set or request the Computer Interface speed.
1607     *
1608     * @param speed 1 is 19,200bps, 2 is 38,400bps, 3 is 57,600bps, 4 is
1609     *              115,200bps. Send invalid speed to request the current
1610     *              setting.
1611     * @return message for set / request interface speed.
1612     */
1613    public static XNetMessage getLISpeedRequestMsg(int speed) {
1614        XNetMessage msg = new XNetMessage(4);
1615        msg.setElement(0, XNetConstants.LI101_REQUEST);
1616        msg.setElement(1, XNetConstants.LI101_REQUEST_BAUD);
1617        msg.setElement(2, speed);
1618        msg.setParity(); // Set the parity bit
1619        return msg;
1620    }
1621
1622   /**
1623    * Generate text translations of messages for use in the XpressNet monitor.
1624    *
1625    * @return representation of the XNetMessage as a string.
1626    */
1627    @Override
1628   public String toMonitorString(){
1629        String text;
1630        /* Start decoding messages sent by the computer */
1631        /* Start with LI101F requests */
1632        if (getElement(0) == XNetConstants.LI_VERSION_REQUEST) {
1633            text = Bundle.getMessage("XNetMessageRequestLIVersion");
1634        } else if (getElement(0) == XNetConstants.LI101_REQUEST) {
1635            switch (getElement(1)) {
1636                case XNetConstants.LI101_REQUEST_ADDRESS:
1637                    text = Bundle.getMessage("XNetMessageRequestLIAddress", getElement(2));
1638                    break;
1639                case XNetConstants.LI101_REQUEST_BAUD:
1640                    switch (getElement(2)) {
1641                        case 1:
1642                            text = Bundle.getMessage(X_NET_MESSAGE_REQUEST_LI_BAUD,
1643                                   Bundle.getMessage("LIBaud19200"));
1644                            break;
1645                        case 2:
1646                            text = Bundle.getMessage(X_NET_MESSAGE_REQUEST_LI_BAUD,
1647                                   Bundle.getMessage("Baud38400"));
1648                            break;
1649                        case 3:
1650                            text = Bundle.getMessage(X_NET_MESSAGE_REQUEST_LI_BAUD,
1651                                   Bundle.getMessage("Baud57600"));
1652                            break;
1653                        case 4:
1654                            text = Bundle.getMessage(X_NET_MESSAGE_REQUEST_LI_BAUD,
1655                                   Bundle.getMessage("Baud115200"));
1656                            break;
1657                        default:
1658                            text = Bundle.getMessage(X_NET_MESSAGE_REQUEST_LI_BAUD,
1659                                   Bundle.getMessage("BaudOther"));
1660                    }
1661                    break;
1662                default:
1663                    text = toString();
1664            }
1665            /* Next, we have generic requests */
1666        } else if (getElement(0) == XNetConstants.CS_REQUEST) {
1667            switch (getElement(1)) {
1668                case XNetConstants.EMERGENCY_OFF:
1669                    text = Bundle.getMessage("XNetMessageRequestEmergencyOff");
1670                    break;
1671                case XNetConstants.RESUME_OPS:
1672                    text = Bundle.getMessage("XNetMessageRequestNormalOps");
1673                    break;
1674                case XNetConstants.SERVICE_MODE_CSRESULT:
1675                    text = Bundle.getMessage("XNetMessageRequestServiceModeResult");
1676                    break;
1677                case XNetConstants.CS_VERSION:
1678                    text = Bundle.getMessage("XNetMessageRequestCSVersion");
1679                    break;
1680                case XNetConstants.CS_STATUS:
1681                    text = Bundle.getMessage("XNetMessageRequestCSStatus");
1682                    break;
1683                default:
1684                    text = toString();
1685            }
1686        } else if (getElement(0) == XNetConstants.CS_SET_POWERMODE
1687                && getElement(1) == XNetConstants.CS_SET_POWERMODE
1688                && getElement(2) == XNetConstants.CS_POWERMODE_AUTO) {
1689            text = Bundle.getMessage("XNetMessageRequestCSPowerModeAuto");
1690        } else if (getElement(0) == XNetConstants.CS_SET_POWERMODE
1691                && getElement(1) == XNetConstants.CS_SET_POWERMODE
1692                && getElement(2) == XNetConstants.CS_POWERMODE_MANUAL) {
1693            text = Bundle.getMessage("XNetMessageRequestCSPowerModeManual");
1694            /* Next, we have Programming Requests */
1695        } else if (getElement(0) == XNetConstants.PROG_READ_REQUEST) {
1696            switch (getElement(1)) {
1697                case XNetConstants.PROG_READ_MODE_REGISTER:
1698                    text = Bundle.getMessage("XNetMessageRequestServiceModeReadRegister",getElement(2));
1699                    break;
1700                case XNetConstants.PROG_READ_MODE_CV:
1701                    text = Bundle.getMessage("XNetMessageRequestServiceModeReadDirect",getElement(2));
1702                    break;
1703                case XNetConstants.PROG_READ_MODE_PAGED:
1704                    text = Bundle.getMessage("XNetMessageRequestServiceModeReadPaged",getElement(2));
1705                    break;
1706                case XNetConstants.PROG_READ_MODE_CV_V36:
1707                    text = Bundle.getMessage(X_NET_MESSAGE_REQUEST_SERVICE_MODE_READ_DIRECT_V_36,(getElement(2)== 0 ? 1024 : getElement(2)));
1708                    break;
1709                case XNetConstants.PROG_READ_MODE_CV_V36 + 1:
1710                    text = Bundle.getMessage(X_NET_MESSAGE_REQUEST_SERVICE_MODE_READ_DIRECT_V_36,(256 + getElement(2)));
1711                    break;
1712                case XNetConstants.PROG_READ_MODE_CV_V36 + 2:
1713                    text = Bundle.getMessage(X_NET_MESSAGE_REQUEST_SERVICE_MODE_READ_DIRECT_V_36,(512 + getElement(2)));
1714                    break;
1715                case XNetConstants.PROG_READ_MODE_CV_V36 + 3:
1716                    text = Bundle.getMessage(X_NET_MESSAGE_REQUEST_SERVICE_MODE_READ_DIRECT_V_36,(768 + getElement(2)));
1717                    break;
1718                default:
1719                    text = toString();
1720            }
1721        } else if (getElement(0) == XNetConstants.PROG_WRITE_REQUEST) {
1722            switch (getElement(1)) {
1723                case XNetConstants.PROG_WRITE_MODE_REGISTER:
1724                    text = Bundle.getMessage("XNetMessageRequestServiceModeWriteRegister",getElement(2),getElement(3));
1725                    break;
1726                case XNetConstants.PROG_WRITE_MODE_CV:
1727                    text = Bundle.getMessage("XNetMessageRequestServiceModeWriteDirect",getElement(2),getElement(3));
1728                    break;
1729                case XNetConstants.PROG_WRITE_MODE_PAGED:
1730                    text = Bundle.getMessage("XNetMessageRequestServiceModeWritePaged",getElement(2),getElement(3));
1731                    break;
1732                case XNetConstants.PROG_WRITE_MODE_CV_V36:
1733                    text = Bundle.getMessage(X_NET_MESSAGE_REQUEST_SERVICE_MODE_WRITE_DIRECT_V_36,(getElement(2)== 0 ? 1024 : getElement(2)),getElement(3));
1734                    break;
1735                case (XNetConstants.PROG_WRITE_MODE_CV_V36 + 1):
1736                    text = Bundle.getMessage(X_NET_MESSAGE_REQUEST_SERVICE_MODE_WRITE_DIRECT_V_36,(256 + getElement(2)),getElement(3));
1737                    break;
1738                case (XNetConstants.PROG_WRITE_MODE_CV_V36 + 2):
1739                    text = Bundle.getMessage(X_NET_MESSAGE_REQUEST_SERVICE_MODE_WRITE_DIRECT_V_36,(512 + getElement(2)),getElement(3));
1740                    break;
1741                case (XNetConstants.PROG_WRITE_MODE_CV_V36 + 3):
1742                    text = Bundle.getMessage(X_NET_MESSAGE_REQUEST_SERVICE_MODE_WRITE_DIRECT_V_36,(768 + getElement(2)),getElement(3));
1743                    break;
1744                default:
1745                    text = toString();
1746            }
1747        } else if (getElement(0) == XNetConstants.OPS_MODE_PROG_REQ) {
1748            switch (getElement(1)) {
1749                case XNetConstants.OPS_MODE_PROG_WRITE_REQ:
1750                    if ((getElement(4) & 0xEC) == 0xEC
1751                            || (getElement(4) & 0xE4) == 0xE4) {
1752                        String message = "";
1753                        if ((getElement(4) & 0xEC) == 0xEC) {
1754                            message ="XNetMessageOpsModeByteWrite";
1755                        } else if ((getElement(4) & 0xE4) == 0xE4) {
1756                            message ="XNetMessageOpsModeByteVerify";
1757                        }
1758                        text = Bundle.getMessage(message,
1759                               getElement(6),
1760                               (1 + getElement(5) + ((getElement(4) & 0x03) << 8)),
1761                               LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)));
1762                        break;
1763                    } else if ((getElement(4) & 0xE8) == 0xE8) {
1764                        String message;
1765                        if ((getElement(6) & 0x10) == 0x10) {
1766                            message ="XNetMessageOpsModeBitVerify";
1767                        } else {
1768                            message ="XNetMessageOpsModeBitWrite";
1769                        }
1770                        text = Bundle.getMessage(message,
1771                                ((getElement(6) & 0x08) >> 3),
1772                                (1 + getElement(5) + ((getElement(4) & 0x03) << 8)),
1773                                (getElement(6) & 0x07),
1774                                LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)));
1775                        break;
1776                    }
1777                    //$FALL-THROUGH$
1778                default:
1779                    text = toString();
1780            }
1781            // Next, decode the locomotive operation requests
1782        } else if (getElement(0) == XNetConstants.LOCO_OPER_REQ) {
1783            text = "Mobile Decoder Operations Request: ";
1784            int speed;
1785            String direction;
1786            switch (getElement(1)) {
1787                case XNetConstants.LOCO_SPEED_14:
1788                    if ((getElement(4) & 0x80) != 0) {
1789                        direction = Bundle.getMessage(FORWARD);
1790                    } else {
1791                        direction = Bundle.getMessage(REVERSE);
1792                    }
1793                    text = text
1794                            + Bundle.getMessage(X_NET_MESSAGE_SET_SPEED,
1795                            LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)))
1796                            + " " + (getElement(4) & 0x0f)
1797                            + " " + Bundle.getMessage(X_NET_MESSAGE_SET_DIRECTION, direction);
1798                    text += " " + Bundle.getMessage(SPEED_STEP_MODE_X, 14) + ".";
1799                    break;
1800                case XNetConstants.LOCO_SPEED_27:
1801                    text = text
1802                            + Bundle.getMessage(X_NET_MESSAGE_SET_SPEED,
1803                            LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)))
1804                            + " ";
1805                    if(log.isDebugEnabled()) {
1806                        log.debug("LOCO_SPEED_27 {} {}", LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)), text);
1807                        // address printed as: "1234" = OK
1808                        // address printed as: "1,234" = WRONG
1809                    }
1810                    speed
1811                            = (((getElement(4) & 0x10) >> 4)
1812                            + ((getElement(4) & 0x0F) << 1));
1813                    if (speed >= 3) {
1814                        speed -= 3;
1815                    }
1816                    text += speed;
1817                    if ((getElement(4) & 0x80) != 0) {
1818                        text += " " + Bundle.getMessage(X_NET_MESSAGE_SET_DIRECTION, Bundle.getMessage(FORWARD));
1819                    } else {
1820                        text += " " + Bundle.getMessage(X_NET_MESSAGE_SET_DIRECTION, Bundle.getMessage(REVERSE));
1821                    }
1822                    text += " " + Bundle.getMessage(SPEED_STEP_MODE_X, 27) + ".";
1823                    break;
1824                case XNetConstants.LOCO_SPEED_28:
1825                    text = text
1826                            + Bundle.getMessage(X_NET_MESSAGE_SET_SPEED,
1827                            LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)))
1828                            + " ";
1829                    speed
1830                            = (((getElement(4) & 0x10) >> 4)
1831                            + ((getElement(4) & 0x0F) << 1));
1832                    if (speed >= 3) {
1833                        speed -= 3;
1834                    }
1835                    text += speed;
1836                    if ((getElement(4) & 0x80) != 0) {
1837                        text += " " + Bundle.getMessage(X_NET_MESSAGE_SET_DIRECTION, Bundle.getMessage(FORWARD));
1838                    } else {
1839                        text += " " + Bundle.getMessage(X_NET_MESSAGE_SET_DIRECTION, Bundle.getMessage(REVERSE));
1840                    }
1841                    text += " " + Bundle.getMessage(SPEED_STEP_MODE_X, 28) + ".";
1842                    break;
1843                case XNetConstants.LOCO_SPEED_128:
1844                    if ((getElement(4) & 0x80) != 0) {
1845                        direction = Bundle.getMessage(FORWARD);
1846                    } else {
1847                        direction = Bundle.getMessage(REVERSE);
1848                    }
1849                    text = text
1850                            + Bundle.getMessage(X_NET_MESSAGE_SET_SPEED,
1851                            LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)))
1852                            + " "
1853                            + (getElement(4) & 0x7F) + " " + Bundle.getMessage(X_NET_MESSAGE_SET_DIRECTION, direction);
1854                    text += " " + Bundle.getMessage(SPEED_STEP_MODE_X, 128) + ".";
1855                    break;
1856                case XNetConstants.LOCO_SET_FUNC_GROUP1:
1857                    text = text + buildSetFunctionGroup1MonitorString();
1858                    break;
1859                case XNetConstants.LOCO_SET_FUNC_GROUP2:
1860                    text = text + buildSetFunctionGroup2MonitorString();
1861                    break;
1862                case XNetConstants.LOCO_SET_FUNC_GROUP3:
1863                    text = text + buildSetFunctionGroup3MonitorString();
1864                    break;
1865                case XNetConstants.LOCO_SET_FUNC_GROUP4:
1866                    text = text + buildSetFunctionGroup4MonitorString();
1867                    break;
1868                case XNetConstants.LOCO_SET_FUNC_GROUP5:
1869                    text = text + buildSetFunctionGroup5MonitorString();
1870                    break;
1871                case XNetConstants.LOCO_SET_FUNC_GROUP6:
1872                    text = text + buildSetFunctionGroup6MonitorString();
1873                    break;
1874                case XNetConstants.LOCO_SET_FUNC_GROUP7:
1875                    text = text + buildSetFunctionGroup7MonitorString();
1876                    break;
1877                case XNetConstants.LOCO_SET_FUNC_GROUP8:
1878                    text = text + buildSetFunctionGroup8MonitorString();
1879                    break;
1880                case XNetConstants.LOCO_SET_FUNC_GROUP9:
1881                    text = text + buildSetFunctionGroup9MonitorString();
1882                    break;
1883                case XNetConstants.LOCO_SET_FUNC_GROUP10:
1884                    text = text + buildSetFunctionGroup10MonitorString();
1885                    break;
1886                case XNetConstants.LOCO_SET_FUNC_GROUP1_MOMENTARY:
1887                    text = text + buildSetFunctionGroup1MomentaryMonitorString();
1888                    break;
1889                case XNetConstants.LOCO_SET_FUNC_GROUP2_MOMENTARY:
1890                    text = text + buildSetFunctionGroup2MomentaryMonitorString();
1891                    break;
1892                case XNetConstants.LOCO_SET_FUNC_GROUP3_MOMENTARY:
1893                    text = text + buildSetFunctionGroup3MomentaryMonitorString();
1894                    break;
1895                case XNetConstants.LOCO_SET_FUNC_GROUP4_MOMENTARY:
1896                    text = text + buildSetFunctionGroup4MomentaryMonitorString();
1897                    break;
1898                case XNetConstants.LOCO_SET_FUNC_GROUP5_MOMENTARY:
1899                    text = text + buildSetFunctionGroup5MomentaryMonitorString();
1900                    break;
1901                case XNetConstants.LOCO_SET_FUNC_GROUP6_MOMENTARY:
1902                    text = text + buildSetFunctionGroup6MomentaryMonitorString();
1903                    break;
1904                case XNetConstants.LOCO_SET_FUNC_GROUP7_MOMENTARY:
1905                    text = text + buildSetFunctionGroup7MomentaryMonitorString();
1906                    break;
1907                case XNetConstants.LOCO_SET_FUNC_GROUP8_MOMENTARY:
1908                    text = text + buildSetFunctionGroup8MomentaryMonitorString();
1909                    break;
1910                case XNetConstants.LOCO_SET_FUNC_GROUP9_MOMENTARY:
1911                    text = text + buildSetFunctionGroup9MomentaryMonitorString();
1912                    break;
1913                case XNetConstants.LOCO_SET_FUNC_GROUP10_MOMENTARY:
1914                    text = text + buildSetFunctionGroup10MomentaryMonitorString();
1915                    break;
1916                case XNetConstants.LOCO_ADD_MULTI_UNIT_REQ:
1917                    text = Bundle.getMessage("XNetMessageAddToConsistDirNormalRequest",
1918                           LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)),
1919                           getElement(4));
1920                    break;
1921                case (XNetConstants.LOCO_ADD_MULTI_UNIT_REQ | 0x01):
1922                    text = Bundle.getMessage("XNetMessageAddToConsistDirReverseRequest",
1923                           LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)),
1924                           getElement(4));
1925                    break;
1926                case (XNetConstants.LOCO_REM_MULTI_UNIT_REQ):
1927                    text = Bundle.getMessage("XNetMessageRemoveFromConsistRequest",
1928                           LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)),
1929                           getElement(4));
1930                    break;
1931                case (XNetConstants.LOCO_IN_MULTI_UNIT_REQ_FORWARD):
1932                    text = Bundle.getMessage("XNetMessageSearchCSStackForwardNextMULoco",
1933                           getElement(2),
1934                           LenzCommandStation.calcLocoAddress(getElement(3), getElement(4)));
1935                    break;
1936                case (XNetConstants.LOCO_IN_MULTI_UNIT_REQ_BACKWARD):
1937                    text = Bundle.getMessage("XNetMessageSearchCSStackBackwardNextMULoco",
1938                           getElement(2),
1939                           LenzCommandStation.calcLocoAddress(getElement(3), getElement(4)));
1940                    break;
1941                default:
1942                    text = toString();
1943            }
1944            // Emergency Stop a locomotive
1945        } else if (getElement(0) == XNetConstants.EMERGENCY_STOP) {
1946            text = Bundle.getMessage("XNetMessageAddressedEmergencyStopRequest",
1947              LenzCommandStation.calcLocoAddress(getElement(1), getElement(2)));
1948            // Disolve or Establish a Double Header
1949        } else if (getElement(0) == XNetConstants.LOCO_DOUBLEHEAD
1950                && getElement(1) == XNetConstants.LOCO_DOUBLEHEAD_BYTE2) {
1951            int loco1 = LenzCommandStation.calcLocoAddress(getElement(2), getElement(3));
1952            int loco2 = LenzCommandStation.calcLocoAddress(getElement(4), getElement(5));
1953            if (loco2 == 0) {
1954                text = Bundle.getMessage("XNetMessageDisolveDoubleHeaderRequest",loco1);
1955            } else {
1956                text = Bundle.getMessage("XNetMessageBuildDoubleHeaderRequest",loco1,loco2);
1957            }
1958            // Locomotive Status Request messages
1959        } else if (getElement(0) == XNetConstants.LOCO_STATUS_REQ) {
1960            switch (getElement(1)) {
1961                case XNetConstants.LOCO_INFO_REQ_FUNC:
1962                    text = Bundle.getMessage("XNetMessageRequestLocoFunctionMomStatus",
1963                            LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)));
1964                    break;
1965                case XNetConstants.LOCO_INFO_REQ_FUNC_HI_ON:
1966                    text = Bundle.getMessage("XNetMessageRequestLocoFunctionHighStatus",
1967                            LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)));
1968                    break;
1969                case XNetConstants.LOCO_INFO_REQ_FUNC_HI_MOM:
1970                    text = Bundle.getMessage("XNetMessageRequestLocoFunctionHighMomStatus",
1971                            LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)));
1972                    break;
1973                case XNetConstants.LOCO_INFO_REQ_V3:
1974                    text = Bundle.getMessage("XNetMessageRequestLocoInfo",
1975                            LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)));
1976                    break;
1977                case XNetConstants.LOCO_STACK_SEARCH_FWD:
1978                    text = Bundle.getMessage("XNetMessageSearchCSStackForward",
1979                           LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)));
1980                    break;
1981                case XNetConstants.LOCO_STACK_SEARCH_BKWD:
1982                    text = Bundle.getMessage("XNetMessageSearchCSStackBackward",
1983                           LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)));
1984                    break;
1985                case XNetConstants.LOCO_STACK_DELETE:
1986                    text = Bundle.getMessage("XNetMessageDeleteAddressOnStack",
1987                            LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)));
1988                    break;
1989                default:
1990                    text = toString();
1991            }
1992        } else if(getElement(0) == XNetConstants.CS_MULTI_UNIT_REQ) {
1993            if (getElement(1) == XNetConstants.CS_MULTI_UNIT_REQ_FWD){
1994                text = Bundle.getMessage("XNetMessageSearchCSStackForwardConsistAddress",
1995                           getElement(2));
1996            } else if(getElement(1) == XNetConstants.CS_MULTI_UNIT_REQ_BKWD){
1997                text = Bundle.getMessage("XNetMessageSearchCSStackBackwardConsistAddress",
1998                           getElement(2));
1999            } else {
2000                    text = toString();
2001            }
2002            // Accessory Info Request message
2003        } else if (getElement(0) == XNetConstants.ACC_INFO_REQ) {
2004            String nibblekey=(((getElement(2) & 0x01) == 0x01) ? "FeedbackEncoderUpperNibble" : "FeedbackEncoderLowerNibble");
2005            text = Bundle.getMessage("XNetMessageFeedbackRequest",
2006                       getElement(1),
2007                       Bundle.getMessage(nibblekey));
2008        } else if (getElement(0) == XNetConstants.ACC_OPER_REQ) {
2009            String messageKey =(((getElement(2) & 0x08) == 0x08) ? "XNetMessageAccessoryDecoderOnRequest" : "XNetMessageAccessoryDecoderOffRequest");
2010            int baseaddress = getElement(1);
2011            int subaddress = ((getElement(2) & 0x06) >> 1);
2012            int address = (baseaddress * 4) + subaddress + 1;
2013            int output = (getElement(2) & 0x01);
2014            text = Bundle.getMessage(messageKey,address, baseaddress,subaddress,output);
2015        } else if (getElement(0) == XNetConstants.ALL_ESTOP) {
2016            text = Bundle.getMessage("XNetMessageRequestEmergencyStop");
2017        } else {
2018            text = toString();
2019        }
2020        return text;
2021   }
2022
2023   private String buildSetFunctionGroup1MonitorString() {
2024       String text = Bundle.getMessage(X_NET_MESSAGE_SET_FUNCTION_GROUP_X, 1) +
2025               " " + LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)) + " ";
2026       int element4 = getElement(4);
2027       if ((element4 & 0x10) != 0) {
2028           text += "F0 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2029       } else {
2030           text += "F0 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2031       }
2032       if ((element4 & 0x01) != 0) {
2033           text += "F1 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2034       } else {
2035           text += "F1 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2036       }
2037       if ((element4 & 0x02) != 0) {
2038           text += "F2 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2039       } else {
2040           text += "F2 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2041       }
2042       if ((element4 & 0x04) != 0) {
2043           text += "F3 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2044       } else {
2045           text += "F3 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2046       }
2047       if ((element4 & 0x08) != 0) {
2048           text += "F4 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2049       } else {
2050           text += "F4 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2051
2052       }
2053       return text;
2054   }
2055
2056   private String buildSetFunctionGroup2MonitorString(){
2057       String text = Bundle.getMessage(X_NET_MESSAGE_SET_FUNCTION_GROUP_X, 2) + " "
2058               + LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)) + " ";
2059       int element4 = getElement(4);
2060       if ((element4 & 0x01) != 0) {
2061           text += "F5 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2062       } else {
2063           text += "F5 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2064       }
2065       if ((element4 & 0x02) != 0) {
2066           text += "F6 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2067       } else {
2068           text += "F6 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2069       }
2070       if ((element4 & 0x04) != 0) {
2071           text += "F7 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2072       } else {
2073           text += "F7 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2074       }
2075       if ((element4 & 0x08) != 0) {
2076           text += "F8 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2077       } else {
2078           text += "F8 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2079       }
2080       return text;
2081   }
2082
2083    private String buildSetFunctionGroup3MonitorString() {
2084        String text = Bundle.getMessage(X_NET_MESSAGE_SET_FUNCTION_GROUP_X, 3) + " "
2085                + LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)) + " ";
2086        int element4 = getElement(4);
2087        if ((element4 & 0x01) != 0) {
2088            text += "F9 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2089        } else {
2090            text += "F9 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2091        }
2092        if ((element4 & 0x02) != 0) {
2093            text += "F10 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2094        } else {
2095            text += "F10 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2096        }
2097        if ((element4 & 0x04) != 0) {
2098            text += "F11 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2099        } else {
2100            text += "F11 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2101        }
2102        if ((element4 & 0x08) != 0) {
2103            text += "F12 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2104        } else {
2105            text += "F12 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2106        }
2107        return text;
2108    }
2109
2110    private String buildSetFunctionGroup4MonitorString() {
2111        String text = Bundle.getMessage(X_NET_MESSAGE_SET_FUNCTION_GROUP_X, 4) + " " + LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)) + " ";
2112        int element4 = getElement(4);
2113        if ((element4 & 0x01) != 0) {
2114            text += "F13 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2115        } else {
2116            text += "F13 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2117        }
2118        if ((element4 & 0x02) != 0) {
2119            text += "F14 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2120        } else {
2121            text += "F14 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2122        }
2123        if ((element4 & 0x04) != 0) {
2124            text += "F15 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2125        } else {
2126            text += "F15 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2127        }
2128        if ((element4 & 0x08) != 0) {
2129            text += "F16 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2130        } else {
2131            text += "F16 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2132        }
2133        if ((element4 & 0x10) != 0) {
2134            text += "F17 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2135        } else {
2136            text += "F17 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2137        }
2138        if ((element4 & 0x20) != 0) {
2139            text += "F18 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2140        } else {
2141            text += "F18 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2142        }
2143        if ((element4 & 0x40) != 0) {
2144            text += "F19 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2145        } else {
2146            text += "F19 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2147        }
2148        if ((element4 & 0x80) != 0) {
2149            text += "F20 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2150        } else {
2151            text += "F20 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2152        }
2153        return text;
2154    }
2155
2156    private String buildSetFunctionGroup5MonitorString() {
2157        String text = Bundle.getMessage(X_NET_MESSAGE_SET_FUNCTION_GROUP_X, 5) + " " + LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)) + " ";
2158        int element4 = getElement(4);
2159        if ((element4 & 0x01) != 0) {
2160            text += "F21 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2161        } else {
2162            text += "F21 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2163        }
2164        if ((element4 & 0x02) != 0) {
2165            text += "F22 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2166        } else {
2167            text += "F22 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2168        }
2169        if ((element4 & 0x04) != 0) {
2170            text += "F23 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2171        } else {
2172            text += "F23 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2173        }
2174        if ((element4 & 0x08) != 0) {
2175            text += "F24 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2176        } else {
2177            text += "F24 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2178        }
2179        if ((element4 & 0x10) != 0) {
2180            text += "F25 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2181        } else {
2182            text += "F25 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2183        }
2184        if ((element4 & 0x20) != 0) {
2185            text += "F26 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2186        } else {
2187            text += "F26 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2188        }
2189        if ((element4 & 0x40) != 0) {
2190            text += "F27 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2191        } else {
2192            text += "F27 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2193        }
2194        if ((element4 & 0x80) != 0) {
2195            text += "F28 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2196        } else {
2197            text += "F28 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2198        }
2199        return text;
2200    }
2201
2202    private String buildSetFunctionGroup6MonitorString() {
2203        String text = Bundle.getMessage(X_NET_MESSAGE_SET_FUNCTION_GROUP_X, 6) + " " + LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)) + " ";
2204        int element4 = getElement(4);
2205        if ((element4 & 0x01) != 0) {
2206            text += "F29 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2207        } else {
2208            text += "F29 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2209        }
2210        if ((element4 & 0x02) != 0) {
2211            text += "F30 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2212        } else {
2213            text += "F30 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2214        }
2215        if ((element4 & 0x04) != 0) {
2216            text += "F31 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2217        } else {
2218            text += "F31 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2219        }
2220        if ((element4 & 0x08) != 0) {
2221            text += "F32 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2222        } else {
2223            text += "F32 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2224        }
2225        if ((element4 & 0x10) != 0) {
2226            text += "F33 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2227        } else {
2228            text += "F33 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2229        }
2230        if ((element4 & 0x20) != 0) {
2231            text += "F34 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2232        } else {
2233            text += "F34 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2234        }
2235        if ((element4 & 0x40) != 0) {
2236            text += "F35 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2237        } else {
2238            text += "F35 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2239        }
2240        if ((element4 & 0x80) != 0) {
2241            text += "F36 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2242        } else {
2243            text += "F36 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2244        }
2245        return text;
2246    }
2247
2248    private String buildSetFunctionGroup7MonitorString() {
2249        String text = Bundle.getMessage(X_NET_MESSAGE_SET_FUNCTION_GROUP_X, 7) + " " + LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)) + " ";
2250        int element4 = getElement(4);
2251        if ((element4 & 0x01) != 0) {
2252            text += "F37 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2253        } else {
2254            text += "F37 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2255        }
2256        if ((element4 & 0x02) != 0) {
2257            text += "F38 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2258        } else {
2259            text += "F38 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2260        }
2261        if ((element4 & 0x04) != 0) {
2262            text += "F39 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2263        } else {
2264            text += "F39 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2265        }
2266        if ((element4 & 0x08) != 0) {
2267            text += "F40 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2268        } else {
2269            text += "F40 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2270        }
2271        if ((element4 & 0x10) != 0) {
2272            text += "F41 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2273        } else {
2274            text += "F41 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2275        }
2276        if ((element4 & 0x20) != 0) {
2277            text += "F42 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2278        } else {
2279            text += "F42 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2280        }
2281        if ((element4 & 0x40) != 0) {
2282            text += "F43 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2283        } else {
2284            text += "F43 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2285        }
2286        if ((element4 & 0x80) != 0) {
2287            text += "F44 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2288        } else {
2289            text += "F44 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2290        }
2291        return text;
2292    }
2293
2294    private String buildSetFunctionGroup8MonitorString() {
2295        String text = Bundle.getMessage(X_NET_MESSAGE_SET_FUNCTION_GROUP_X, 8) + " " + LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)) + " ";
2296        int element4 = getElement(4);
2297        if ((element4 & 0x01) != 0) {
2298            text += "F45 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2299        } else {
2300            text += "F45 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2301        }
2302        if ((element4 & 0x02) != 0) {
2303            text += "F46 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2304        } else {
2305            text += "F46 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2306        }
2307        if ((element4 & 0x04) != 0) {
2308            text += "F47 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2309        } else {
2310            text += "F47 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2311        }
2312        if ((element4 & 0x08) != 0) {
2313            text += "F48 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2314        } else {
2315            text += "F48 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2316        }
2317        if ((element4 & 0x10) != 0) {
2318            text += "F49 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2319        } else {
2320            text += "F49 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2321        }
2322        if ((element4 & 0x20) != 0) {
2323            text += "F50 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2324        } else {
2325            text += "F50 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2326        }
2327        if ((element4 & 0x40) != 0) {
2328            text += "F51 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2329        } else {
2330            text += "F51 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2331        }
2332        if ((element4 & 0x80) != 0) {
2333            text += "F52 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2334        } else {
2335            text += "F52 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2336        }
2337        return text;
2338    }
2339
2340    private String buildSetFunctionGroup9MonitorString() {
2341        String text = Bundle.getMessage(X_NET_MESSAGE_SET_FUNCTION_GROUP_X, 9) + " " + LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)) + " ";
2342        int element4 = getElement(4);
2343        if ((element4 & 0x01) != 0) {
2344            text += "F53 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2345        } else {
2346            text += "F53 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2347        }
2348        if ((element4 & 0x02) != 0) {
2349            text += "F54 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2350        } else {
2351            text += "F54 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2352        }
2353        if ((element4 & 0x04) != 0) {
2354            text += "F55 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2355        } else {
2356            text += "F55 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2357        }
2358        if ((element4 & 0x08) != 0) {
2359            text += "F56 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2360        } else {
2361            text += "F56 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2362        }
2363        if ((element4 & 0x10) != 0) {
2364            text += "F57 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2365        } else {
2366            text += "F57 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2367        }
2368        if ((element4 & 0x20) != 0) {
2369            text += "F58 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2370        } else {
2371            text += "F58 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2372        }
2373        if ((element4 & 0x40) != 0) {
2374            text += "F59 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2375        } else {
2376            text += "F59 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2377        }
2378        if ((element4 & 0x80) != 0) {
2379            text += "F60 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2380        } else {
2381            text += "F60 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2382        }
2383        return text;
2384    }
2385
2386    private String buildSetFunctionGroup10MonitorString() {
2387        String text = Bundle.getMessage(X_NET_MESSAGE_SET_FUNCTION_GROUP_X, 10) + " " + LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)) + " ";
2388        int element4 = getElement(4);
2389        if ((element4 & 0x01) != 0) {
2390            text += "F61 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2391        } else {
2392            text += "F61 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2393        }
2394        if ((element4 & 0x02) != 0) {
2395            text += "F62 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2396        } else {
2397            text += "F62 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2398        }
2399        if ((element4 & 0x04) != 0) {
2400            text += "F63 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2401        } else {
2402            text += "F63 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2403        }
2404        if ((element4 & 0x08) != 0) {
2405            text += "F64 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2406        } else {
2407            text += "F64 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2408        }
2409        if ((element4 & 0x10) != 0) {
2410            text += "F65 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2411        } else {
2412            text += "F65 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2413        }
2414        if ((element4 & 0x20) != 0) {
2415            text += "F66 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2416        } else {
2417            text += "F66 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2418        }
2419        if ((element4 & 0x40) != 0) {
2420            text += "F67 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2421        } else {
2422            text += "F67 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2423        }
2424        if ((element4 & 0x80) != 0) {
2425            text += "F68 " + Bundle.getMessage(POWER_STATE_ON) + "; ";
2426        } else {
2427            text += "F68 " + Bundle.getMessage(POWER_STATE_OFF) + "; ";
2428        }
2429        return text;
2430    }
2431
2432
2433
2434    private String buildSetFunctionGroup1MomentaryMonitorString() {
2435            String text = Bundle.getMessage(X_NET_MESSAGE_SET_FUNCTION_GROUP_X_MOMENTARY, 1) + " "
2436                + LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)) + " ";
2437        int element4 = getElement(4);
2438        if ((element4 & 0x10) == 0) {
2439            text += "F0 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2440        } else {
2441            text += "F0 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2442        }
2443        if ((element4 & 0x01) == 0) {
2444            text += "F1 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2445        } else {
2446            text += "F1 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2447        }
2448        if ((element4 & 0x02) == 0) {
2449            text += "F2 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2450        } else {
2451            text += "F2 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2452        }
2453        if ((element4 & 0x04) == 0) {
2454            text += "F3 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2455        } else {
2456            text += "F3 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2457        }
2458        if ((element4 & 0x08) == 0) {
2459            text += "F4 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2460        } else {
2461            text += "F4 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2462        }
2463        return text;
2464    }
2465
2466    private String buildSetFunctionGroup2MomentaryMonitorString() {
2467        String text = Bundle.getMessage(X_NET_MESSAGE_SET_FUNCTION_GROUP_X_MOMENTARY, 2) + " "
2468                + LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)) + " ";
2469        int element4 = getElement(4);
2470        if ((element4 & 0x01) == 0) {
2471            text += "F5 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2472        } else {
2473            text += "F5 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2474        }
2475        if ((element4 & 0x02) == 0) {
2476            text += "F6 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2477        } else {
2478            text += "F6 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2479        }
2480        if ((element4 & 0x04) == 0) {
2481            text += "F7 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2482        } else {
2483            text += "F7 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2484        }
2485        if ((element4 & 0x08) == 0) {
2486            text += "F8 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2487        } else {
2488            text += "F8 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2489        }
2490        return text;
2491    }
2492
2493    private String buildSetFunctionGroup3MomentaryMonitorString() {
2494        String text = Bundle.getMessage(X_NET_MESSAGE_SET_FUNCTION_GROUP_X_MOMENTARY, 3) + " " + LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)) + " ";
2495        int element4 = getElement(4);
2496        if ((element4 & 0x01) == 0) {
2497            text += "F9 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2498        } else {
2499            text += "F9 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2500        }
2501        if ((element4 & 0x02) == 0) {
2502            text += "F10 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2503        } else {
2504            text += "F10 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2505        }
2506        if ((element4 & 0x04) == 0) {
2507            text += "F11 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2508        } else {
2509            text += "F11 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2510        }
2511        if ((element4 & 0x08) == 0) {
2512            text += "F12 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2513        } else {
2514            text += "F12 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2515        }
2516        return text;
2517    }
2518
2519    private String buildSetFunctionGroup4MomentaryMonitorString() {
2520        String text = Bundle.getMessage(X_NET_MESSAGE_SET_FUNCTION_GROUP_X_MOMENTARY, 4) + " "
2521                + LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)) + " ";
2522        int element4 = getElement(4);
2523        if ((element4 & 0x01) == 0) {
2524            text += "F13 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2525        } else {
2526            text += "F13 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2527        }
2528        if ((element4 & 0x02) == 0) {
2529            text += "F14 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2530        } else {
2531            text += "F14 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2532        }
2533        if ((element4 & 0x04) == 0) {
2534            text += "F15 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2535        } else {
2536            text += "F15 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2537        }
2538        if ((element4 & 0x08) == 0) {
2539            text += "F16 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2540        } else {
2541            text += "F16 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2542        }
2543        if ((element4 & 0x10) == 0) {
2544            text += "F17 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2545        } else {
2546            text += "F17 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2547        }
2548        if ((element4 & 0x20) == 0) {
2549            text += "F18 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2550        } else {
2551            text += "F18 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2552        }
2553        if ((element4 & 0x40) == 0) {
2554            text += "F19 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2555        } else {
2556            text += "F19 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2557        }
2558        if ((element4 & 0x80) == 0) {
2559            text += "F20 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2560        } else {
2561            text += "F20 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2562        }
2563        return text;
2564    }
2565
2566    private String buildSetFunctionGroup5MomentaryMonitorString() {
2567        String text = Bundle.getMessage(X_NET_MESSAGE_SET_FUNCTION_GROUP_X_MOMENTARY, 5) + " " + LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)) + " ";
2568        int element4 = getElement(4);
2569        if ((element4 & 0x01) == 0) {
2570            text += "F21 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2571        } else {
2572            text += "F21 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2573        }
2574        if ((element4 & 0x02) == 0) {
2575            text += "F22 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2576        } else {
2577            text += "F22 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2578        }
2579        if ((element4 & 0x04) == 0) {
2580            text += "F23 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2581        } else {
2582            text += "F23 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2583        }
2584        if ((element4 & 0x08) == 0) {
2585            text += "F24 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2586        } else {
2587            text += "F24 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2588        }
2589        if ((element4 & 0x10) == 0) {
2590            text += "F25 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2591        } else {
2592            text += "F25 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2593        }
2594        if ((element4 & 0x20) == 0) {
2595            text += "F26 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2596        } else {
2597            text += "F26 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2598        }
2599        if ((element4 & 0x40) == 0) {
2600            text += "F27 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2601        } else {
2602            text += "F27 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2603        }
2604        if ((element4 & 0x80) == 0) {
2605            text += "F28 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2606        } else {
2607            text += "F28 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2608        }
2609        return text;
2610    }
2611
2612    private String buildSetFunctionGroup6MomentaryMonitorString() {
2613        String text = Bundle.getMessage(X_NET_MESSAGE_SET_FUNCTION_GROUP_X_MOMENTARY, 6) + " " + LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)) + " ";
2614        int element4 = getElement(4);
2615        if ((element4 & 0x01) == 0) {
2616            text += "F29 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2617        } else {
2618            text += "F29 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2619        }
2620        if ((element4 & 0x02) == 0) {
2621            text += "F30 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2622        } else {
2623            text += "F30 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2624        }
2625        if ((element4 & 0x04) == 0) {
2626            text += "F31 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2627        } else {
2628            text += "F31 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2629        }
2630        if ((element4 & 0x08) == 0) {
2631            text += "F32 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2632        } else {
2633            text += "F32 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2634        }
2635        if ((element4 & 0x10) == 0) {
2636            text += "F33 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2637        } else {
2638            text += "F33 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2639        }
2640        if ((element4 & 0x20) == 0) {
2641            text += "F34 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2642        } else {
2643            text += "F34 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2644        }
2645        if ((element4 & 0x40) == 0) {
2646            text += "F35 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2647        } else {
2648            text += "F35 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2649        }
2650        if ((element4 & 0x80) == 0) {
2651            text += "F36 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2652        } else {
2653            text += "F36 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2654        }
2655        return text;
2656    }
2657
2658    private String buildSetFunctionGroup7MomentaryMonitorString() {
2659        String text = Bundle.getMessage(X_NET_MESSAGE_SET_FUNCTION_GROUP_X_MOMENTARY, 7) + " " + LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)) + " ";
2660        int element4 = getElement(4);
2661        if ((element4 & 0x01) == 0) {
2662            text += "F37 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2663        } else {
2664            text += "F37 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2665        }
2666        if ((element4 & 0x02) == 0) {
2667            text += "F38 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2668        } else {
2669            text += "F38 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2670        }
2671        if ((element4 & 0x04) == 0) {
2672            text += "F39 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2673        } else {
2674            text += "F39 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2675        }
2676        if ((element4 & 0x08) == 0) {
2677            text += "F40 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2678        } else {
2679            text += "F40 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2680        }
2681        if ((element4 & 0x10) == 0) {
2682            text += "F41 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2683        } else {
2684            text += "F41 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2685        }
2686        if ((element4 & 0x20) == 0) {
2687            text += "F42 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2688        } else {
2689            text += "F42 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2690        }
2691        if ((element4 & 0x40) == 0) {
2692            text += "F43 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2693        } else {
2694            text += "F43 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2695        }
2696        if ((element4 & 0x80) == 0) {
2697            text += "F44 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2698        } else {
2699            text += "F44 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2700        }
2701        return text;
2702    }
2703
2704    private String buildSetFunctionGroup8MomentaryMonitorString() {
2705        String text = Bundle.getMessage(X_NET_MESSAGE_SET_FUNCTION_GROUP_X_MOMENTARY, 8) + " " + LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)) + " ";
2706        int element4 = getElement(4);
2707        if ((element4 & 0x01) == 0) {
2708            text += "F45 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2709        } else {
2710            text += "F45 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2711        }
2712        if ((element4 & 0x02) == 0) {
2713            text += "F46 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2714        } else {
2715            text += "F46 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2716        }
2717        if ((element4 & 0x04) == 0) {
2718            text += "F47 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2719        } else {
2720            text += "F47 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2721        }
2722        if ((element4 & 0x08) == 0) {
2723            text += "F48 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2724        } else {
2725            text += "F48 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2726        }
2727        if ((element4 & 0x10) == 0) {
2728            text += "F49 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2729        } else {
2730            text += "F49 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2731        }
2732        if ((element4 & 0x20) == 0) {
2733            text += "F50 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2734        } else {
2735            text += "F50 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2736        }
2737        if ((element4 & 0x40) == 0) {
2738            text += "F51 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2739        } else {
2740            text += "F51 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2741        }
2742        if ((element4 & 0x80) == 0) {
2743            text += "F52 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2744        } else {
2745            text += "F52 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2746        }
2747        return text;
2748    }
2749
2750    private String buildSetFunctionGroup9MomentaryMonitorString() {
2751        String text = Bundle.getMessage(X_NET_MESSAGE_SET_FUNCTION_GROUP_X_MOMENTARY, 9) + " " + LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)) + " ";
2752        int element4 = getElement(4);
2753        if ((element4 & 0x01) == 0) {
2754            text += "F53 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2755        } else {
2756            text += "F53 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2757        }
2758        if ((element4 & 0x02) == 0) {
2759            text += "F54 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2760        } else {
2761            text += "F54 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2762        }
2763        if ((element4 & 0x04) == 0) {
2764            text += "F55 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2765        } else {
2766            text += "F55 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2767        }
2768        if ((element4 & 0x08) == 0) {
2769            text += "F56 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2770        } else {
2771            text += "F56 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2772        }
2773        if ((element4 & 0x10) == 0) {
2774            text += "F57 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2775        } else {
2776            text += "F57 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2777        }
2778        if ((element4 & 0x20) == 0) {
2779            text += "F58 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2780        } else {
2781            text += "F58 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2782        }
2783        if ((element4 & 0x40) == 0) {
2784            text += "F59 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2785        } else {
2786            text += "F59 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2787        }
2788        if ((element4 & 0x80) == 0) {
2789            text += "F60 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2790        } else {
2791            text += "F60 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2792        }
2793        return text;
2794    }
2795
2796    private String buildSetFunctionGroup10MomentaryMonitorString() {
2797        String text = Bundle.getMessage(X_NET_MESSAGE_SET_FUNCTION_GROUP_X_MOMENTARY, 10) + " " + LenzCommandStation.calcLocoAddress(getElement(2), getElement(3)) + " ";
2798        int element4 = getElement(4);
2799        if ((element4 & 0x01) == 0) {
2800            text += "F61 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2801        } else {
2802            text += "F61 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2803        }
2804        if ((element4 & 0x02) == 0) {
2805            text += "F62 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2806        } else {
2807            text += "F62 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2808        }
2809        if ((element4 & 0x04) == 0) {
2810            text += "F63 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2811        } else {
2812            text += "F63 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2813        }
2814        if ((element4 & 0x08) == 0) {
2815            text += "F64 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2816        } else {
2817            text += "F64 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2818        }
2819        if ((element4 & 0x10) == 0) {
2820            text += "F65 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2821        } else {
2822            text += "F65 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2823        }
2824        if ((element4 & 0x20) == 0) {
2825            text += "F66 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2826        } else {
2827            text += "F66 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2828        }
2829        if ((element4 & 0x40) == 0) {
2830            text += "F67 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2831        } else {
2832            text += "F67 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2833        }
2834        if ((element4 & 0x80) == 0) {
2835            text += "F68 " + Bundle.getMessage(FUNCTION_CONTINUOUS) + "; ";
2836        } else {
2837            text += "F68 " + Bundle.getMessage(FUNCTION_MOMENTARY) + "; ";
2838        }
2839        return text;
2840    }
2841
2842    // initialize logging
2843    private static final Logger log = LoggerFactory.getLogger(XNetMessage.class);
2844
2845}