001package jmri.jmrix.xpa;
002
003import java.util.Arrays;
004import org.slf4j.Logger;
005import org.slf4j.LoggerFactory;
006
007/**
008 * Encodes a message to an XpressNet command station via an XPA and a modem.
009 *
010 * @author Paul Bender Copyright (C) 2004
011 */
012public class XpaMessage implements jmri.jmrix.Message {
013
014    public final static int MAX_SIZE = 64;
015
016    private int _nDataChars = 0;
017    private byte[] _dataChars = null;
018
019    // create a new one
020    public XpaMessage(int i) {
021        if (i < 1) {
022            log.error("invalid length in call to ctor");
023        }
024        _nDataChars = i;
025        _dataChars = new byte[i];
026    }
027
028    // create a new one, given a string containing the message.
029    public XpaMessage(String s) {
030        if (s.length() < 1) {
031            log.error("zero length string in call to ctor");
032        }
033        _nDataChars = s.length();
034        _dataChars = s.getBytes();
035    }
036
037    // create a new one with default MAX_SIZE
038    public XpaMessage() {
039        this(MAX_SIZE);
040    }
041
042    // copy one
043    public XpaMessage(XpaMessage m) {
044        if (m == null) {
045            log.error("copy ctor of null message");
046            return;
047        }
048        _nDataChars = m._nDataChars;
049        _dataChars = new byte[_nDataChars];
050        System.arraycopy(m._dataChars, 0, _dataChars, 0, _nDataChars);
051    }
052
053    // compare two XpaMessages.
054    @Override
055    public boolean equals(Object m) {
056        if (m != null && m instanceof XpaMessage
057                && ((XpaMessage) m).getNumDataElements() == this.getNumDataElements()) {
058            return Arrays.equals(((XpaMessage) m)._dataChars, _dataChars);
059        }
060        return false;
061    }
062
063    @Override
064    public int hashCode() {
065        int hash = 5;
066        hash = 79 * hash + this._nDataChars;
067        hash = 79 * hash + Arrays.hashCode(this._dataChars);
068        return hash;
069    }
070
071    // accessors to the bulk data
072    @Override
073    public int getNumDataElements() {
074        return _nDataChars;
075    }
076
077    @Override
078    public int getElement(int n) {
079        return _dataChars[n];
080    }
081
082    @Override
083    public void setElement(int n, int v) {
084        _dataChars[n] = (byte) (v & 0x7F);
085    }
086
087    // display format
088    @Override
089    public String toString() {
090        StringBuilder s = new StringBuilder();
091        for (int i = 0; i < _nDataChars; i++) {
092            s.append((char) _dataChars[i]);
093        }
094        return s.toString();
095    }
096
097    // static methods to return a formatted message
098    static XpaMessage getDefaultInitMsg() {
099        return new XpaMessage("ATX0E0;");
100    }
101
102
103    /* Get a message which sends an Estop or Everything off command
104     to the layout.  This will toggle the Estop commands.
105     XPA settings can change the behavior of this command.  It may
106     only work with a single locomotive, or it may kill the entire
107     layout.
108     */
109    static XpaMessage getEStopMsg() {
110        return new XpaMessage("ATDT0;");
111    }
112
113    // Locomotive Messages
114
115    /*
116     Get a message which sends an "Idle" (zero speed) command
117     to a specific locomotive on the layout.
118     */
119    static XpaMessage getIdleMsg(int address) {
120        return new XpaMessage("ATDT#" + address + "*5;");
121    }
122
123    /**
124     * Get a message for an "Increase Speed" command
125     * to a specific locomotive on the layout.  To make
126     * calculations easy, this uses a single speed step increase.
127     *
128     * @param address   throttle loco address for message
129     * @param steps     amount of speed steps to change to increase
130     * @return message for the requested change
131     */
132    static XpaMessage getIncSpeedMsg(int address, int steps) {
133        StringBuilder buf = new StringBuilder("ATDT#" + address + "*");
134        String message;
135        for (int i = 0; i < steps; i++) {
136            buf.append("3");
137        }
138        message = buf.toString() + ";";
139        return new XpaMessage(message);
140    }
141
142    /**
143     * Get a message for a "Decrease Speed" command
144     * to a specific locomotive on the layout.  To make
145     * calculations easy, this uses a single speed step decrease.
146     *
147     * @param address   throttle loco address for message
148     * @param steps     amount of speed steps to change to decrease
149     * @return message for the requested change
150     */
151    static XpaMessage getDecSpeedMsg(int address, int steps) {
152        StringBuilder buf = new StringBuilder("ATDT#" + address + "*");
153        String Message;
154        for (int i = 0; i < steps; i++) {
155            buf.append("1");
156        }
157        Message = buf.toString() + ";";
158        return new XpaMessage(Message);
159    }
160
161    /*
162     Get a message for a "Direction Forward" command
163     to a specific locomotive on the layout.
164     */
165    static XpaMessage getDirForwardMsg(int address) {
166        return new XpaMessage("ATDT#" + address + "*52;");
167    }
168
169    /*
170     Get a message for a "Direction Reverse" command
171     to a specific locomotive on the layout.
172     */
173    static XpaMessage getDirReverseMsg(int address) {
174        return new XpaMessage("ATDT#" + address + "*58;");
175    }
176
177    /*
178     Get a message which sends a "Toggle Function" command
179     to a specific locomotive on the layout.
180     */
181    static XpaMessage getFunctionMsg(int address, int function) {
182        return new XpaMessage("ATDT#" + address + "**" + function + ";");
183    }
184
185    // Switch Commands
186
187    /*
188     Get a message for a "Switch Position Normal" command
189     to a specific accessory decoder on the layout.
190     */
191    static XpaMessage getSwitchNormalMsg(int address) {
192        return new XpaMessage("ATDT#" + address + "#3;");
193    }
194
195    /*
196     Get a message for a "Switch Position Reverse" command
197     to a specific accessory decoder on the layout.
198     */
199    static XpaMessage getSwitchReverseMsg(int address) {
200        return new XpaMessage("ATDT#" + address + "#1;");
201    }
202
203    // Xpa Device Settings
204    /* Get a message for setting a Device value */
205    public static XpaMessage getDeviceSettingMsg(int setting) {
206        return new XpaMessage("ATDT*" + setting + "*");
207    }
208
209    private final static Logger log = LoggerFactory.getLogger(XpaMessage.class
210            .getName());
211
212}