001package jmri.jmrix.lenz;
002
003import org.slf4j.Logger;
004import org.slf4j.LoggerFactory;
005
006/**
007 * Defines the standard/common routines used in multiple classes related to the
008 * a Lenz Command Station, on an XpressNet network.
009 *
010 * @author Bob Jacobsen Copyright (C) 2001 Portions by Paul Bender Copyright (C) 2003
011 */
012public class LenzCommandStation implements jmri.CommandStation {
013
014    /* The First group of routines is for obtaining the Software and
015     hardware version of the Command station */
016
017    /**
018     * We need to add a few data members for saving the version information we
019     * get from the layout.
020     */
021    private int cmdStationType = -1;
022    private float cmdStationSoftwareVersion = -1;
023    private int cmdStationSoftwareVersionBCD = -1;
024
025    /**
026     * Return the CS Type.
027     * @return CS type.
028     */
029    public int getCommandStationType() {
030        return cmdStationType;
031    }
032
033    /**
034     * Set the CS Type.
035     * @param t CS type.
036     */
037    public void setCommandStationType(int t) {
038        cmdStationType = t;
039    }
040
041    /**
042     * Set the CS Type based on an XpressNet Message.
043     * @param l XNetReply containing the CS type.
044     */
045    public void setCommandStationType(XNetReply l) {
046        if (l.getElement(0) == XNetConstants.CS_SERVICE_MODE_RESPONSE) {
047            // This is the Command Station Software Version Response
048            if (l.getElement(1) == XNetConstants.CS_SOFTWARE_VERSION) {
049                cmdStationType = l.getElement(3);
050            }
051        }
052    }
053
054    /**
055     * Get the CS Software Version.
056     * @return software version.
057     */
058    public float getCommandStationSoftwareVersion() {
059        return cmdStationSoftwareVersion;
060    }
061
062    /**
063     * Get the CS Software Version in BCD (for use in comparisons).
064     * @return software version.
065     */
066    public float getCommandStationSoftwareVersionBCD() {
067        return cmdStationSoftwareVersionBCD;
068    }
069
070    /**
071     * Set the CS Software Version.
072     * @param v software version.
073     */
074    public void setCommandStationSoftwareVersion(float v) {
075        cmdStationSoftwareVersion = v;
076    }
077
078    /**
079     * Set the CS Software Version based on an XpressNet Message.
080     * @param l reply containing CS version.
081     */
082    public void setCommandStationSoftwareVersion(XNetReply l) {
083        if (l.getElement(0) == XNetConstants.CS_SERVICE_MODE_RESPONSE) {
084            // This is the Command Station Software Version Response
085            if (l.getElement(1) == XNetConstants.CS_SOFTWARE_VERSION) {
086                try {
087                    cmdStationSoftwareVersion = (l.getElementBCD(2).floatValue()) / 10;
088                } catch (java.lang.NumberFormatException nfe) {
089                    // the number was not in BCD format as expected.
090                    // the upper nibble is the major version and the lower 
091                    // nibble is the minor version.
092                    cmdStationSoftwareVersion = ((l.getElement(2) & 0xf0) >> 4) + (l.getElement(2) & 0x0f) / 100.0f;
093                }
094                cmdStationSoftwareVersionBCD = l.getElement(2);
095            }
096        }
097    }
098
099    /**
100     * Provide the version string returned during the initial check.
101     * @return human readable version string.
102     */
103    public String getVersionString() {
104        return Bundle.getMessage("CSVersionString", getCommandStationType(),getCommandStationSoftwareVersionBCD());
105    }
106
107    /**
108     * XpressNet command station does provide Ops Mode.
109     *
110     * @return true if CS type 1 or 2, else false.
111     */
112    public boolean isOpsModePossible() {
113        return cmdStationType != 0x01 && cmdStationType != 0x02;
114    }
115
116    // A few utility functions
117
118    /**
119     * Get the Lower byte of a locomotive address from the decimal locomotive
120     * address.
121     * @param address loco address.
122     * @return low address byte including DCC offset.
123     */
124    public static int getDCCAddressLow(int address) {
125        /* For addresses below 100, we just return the address, otherwise,
126         we need to return the upper byte of the address after we add the
127         offset 0xC000. The first address used for addresses over 99 is 0xC064*/
128        if (address < 100) {
129            return (address);
130        } else {
131            int temp = address + 0xC000;
132            temp = temp & 0x00FF;
133            return temp;
134        }
135    }
136
137    /**
138     * Get the Upper byte of a locomotive address from the decimal locomotive
139     * address.
140     * @param address loco address.
141     * @return upper byte after DCC offset.
142     */
143    public static int getDCCAddressHigh(int address) {
144        /* this isn't actually the high byte, For addresses below 100, we
145         just return 0, otherwise, we need to return the upper byte of the
146         address after we add the offset 0xC000 The first address used for
147         addresses over 99 is 0xC064*/
148        if (address < 100) {
149            return (0x00);
150        } else {
151            int temp = address + 0xC000;
152            temp = temp & 0xFF00;
153            temp = temp / 256;
154            return temp;
155        }
156    }
157
158    /**
159     * We need to calculate the locomotive address when doing the translations
160     * back to text. XpressNet Messages will have these as two elements, which
161     * need to get translated back into a single address by reversing the
162     * formulas used to calculate them in the first place.
163     *
164     * @param AH the high order byte of the address
165     * @param AL the low order byte of the address
166     * @return the address as an integer.
167     */
168    static public int calcLocoAddress(int AH, int AL) {
169        if (AH == 0x00) {
170            /* if AH is 0, this is a short address */
171            return (AL);
172        } else {
173            /* This must be a long address */
174            int address;
175            address = ((AH * 256) & 0xFF00);
176            address += (AL & 0xFF);
177            address -= 0xC000;
178            return (address);
179        }
180    }
181
182    /* To Implement the CommandStation Interface, we have to define the 
183     sendPacket function */
184
185    /**
186     * Send a specific packet to the rails.
187     *
188     * @param packet  Byte array representing the packet, including the
189     *                error-correction byte. Must not be null.
190     * @param repeats Number of times to repeat the transmission.
191     */
192    @Override
193    public boolean sendPacket(byte[] packet, int repeats) {
194
195        if (_tc == null) {
196            log.error("Send Packet Called without setting traffic controller");
197            return false;
198        }
199
200        XNetMessage msg = XNetMessage.getNMRAXNetMsg(packet);
201        for (int i = 0; i < repeats; i++) {
202            _tc.sendXNetMessage(msg, null);
203        }
204        return true;
205    }
206
207    /*
208     * For the command station interface, we need to set the traffic 
209     * controller.
210     */
211    public void setTrafficController(XNetTrafficController tc) {
212        _tc = tc;
213    }
214
215    private XNetTrafficController _tc = null;
216
217    public void setSystemConnectionMemo(XNetSystemConnectionMemo memo) {
218        adaptermemo = memo;
219    }
220
221    XNetSystemConnectionMemo adaptermemo;
222
223    @Override
224    public String getUserName() {
225        if (adaptermemo == null) {
226            return Bundle.getMessage("MenuXpressNet");
227        }
228        return adaptermemo.getUserName();
229    }
230
231    @Override
232    public String getSystemPrefix() {
233        if (adaptermemo == null) {
234            return "X";
235        }
236        return adaptermemo.getSystemPrefix();
237    }
238
239    /*
240     * Register for logging.
241     */
242    private static final Logger log = LoggerFactory.getLogger(LenzCommandStation.class);
243
244}