001package jmri.jmrix.marklin;
002
003/**
004 * Encodes a message to a Marklin command station.
005 * <p>
006 * The {@link MarklinReply} class handles the response from the command station.
007 * Packages of length 13 are interpreted as can-bus packages:
008 * 4 bytes Can-bus-ID (BigEndian or network order),
009 * 1-byte length and 
010 * 8 bytes of data, if necessary with null bytes to fill in.
011 * <p>
012 * The message ID is divided into the areas of lower priority (priority),
013 * command (command), response and hash.
014 * The communication is based on the following format:
015 * Prio - 2 +2bit
016 * Command 8 bit
017 * Resp - 1 bit
018 * Hash - 16bit
019 * DLC - 4bit (ie CAN message length)
020 * CAN message 8 BYTES
021 * Can Message Bytes 0 to 3 are the address bytes, with byte 0 High, byte 3 low
022 * @author Kevin Dickerson Copyright (C) 2001, 2008
023 */
024public class MarklinMessage extends jmri.jmrix.AbstractMRMessage {
025
026    static int MY_UID = 0x12345678;
027
028    MarklinMessage() {
029        _dataChars = new int[13];
030        _nDataChars = 13;
031        setBinary(true);
032        for (int i = 0; i < 13; i++) {
033            _dataChars[i] = 0x00;
034        }
035    }
036
037    // create a new one from an array
038    public MarklinMessage(int[] d) {
039        this();
040        System.arraycopy(d, 0, _dataChars, 0, d.length);
041    }
042
043    // create a new one from a byte array, as a service
044    public MarklinMessage(byte[] d) {
045        this();
046        for (int i = 0; i < d.length; i++) {
047            _dataChars[i] = d[i] & 0xFF;
048        }
049    }
050
051    // create a new one
052    public MarklinMessage(int i) {
053        this();
054    }
055
056    // copy one
057    public MarklinMessage(MarklinMessage m) {
058        super(m);
059    }
060
061    // static methods to return a formatted message
062    public static MarklinMessage getEnableMain() {
063        MarklinMessage m = new MarklinMessage();
064        m.setElement(0, MarklinConstants.SYSCOMMANDSTART & 0xFF);
065        m.setElement(1, 0x00 & 0xFF);
066        m.setElement(2, MarklinConstants.HASHBYTE1 & 0xFF);
067        m.setElement(3, MarklinConstants.HASHBYTE2 & 0xFF);
068        m.setElement(4, 0x05 & 0xFF); //five bytes;
069        //5, 6, 7, 8 Address but this is a global command
070        m.setElement(9, MarklinConstants.CMDGOSYS & 0xFF); //Turn main on 0x01
071        return m;
072    }
073
074    public static MarklinMessage getKillMain() {
075        MarklinMessage m = new MarklinMessage();
076        m.setElement(0, MarklinConstants.SYSCOMMANDSTART & 0xFF);
077        m.setElement(1, 0x00 & 0xFF);
078        m.setElement(2, MarklinConstants.HASHBYTE1 & 0xFF);
079        m.setElement(3, MarklinConstants.HASHBYTE2 & 0xFF);
080        m.setElement(4, 0x05 & 0xFF); //five bytes;
081        //5, 6, 7, 8 Address but this is a global command
082        m.setElement(9, MarklinConstants.CMDSTOPSYS & 0xFF); //Turn main off 0x00
083        return m;
084    }
085
086    /**
087     * Generate CAN BOOT command (0xB1).
088     * <p>
089     * This command is used to invoke the bootloader update sequence
090     * for Märklin devices. According to German language forum research,
091     * this is part of the software/bootloader command range used for
092     * firmware updates and device initialization.
093     * 
094     * @return MarklinMessage containing the CAN BOOT command
095     * @see <a href="https://www.stummiforum.de/t122854f7-M-rklin-CAN-Protokoll-x-B-commands-updates.html">Märklin CAN Protokoll 0x1B commands documentation</a>
096     */
097    public static MarklinMessage getCanBoot() {
098        MarklinMessage m = new MarklinMessage();
099        m.setElement(0, (0xB1 >> 7) & 0xFF);  // Command 0xB1 high bits
100        m.setElement(1, (0xB1 << 1) & 0xFF);  // Command 0xB1 low bits
101        m.setElement(2, MarklinConstants.HASHBYTE1 & 0xFF);
102        m.setElement(3, MarklinConstants.HASHBYTE2 & 0xFF);
103        m.setElement(4, 0x00 & 0xFF); // DLC = 0 for basic boot command
104        // Elements 5-12 are left as default 0x00 for this command
105        return m;
106    }
107
108    //static public MarklinMessage get
109    public static MarklinMessage getSetTurnout(int addr, int state, int power) {
110        MarklinMessage m = new MarklinMessage();
111        m.setElement(0, (MarklinConstants.ACCCOMMANDSTART >> 7) & 0xFF);
112        m.setElement(1, (MarklinConstants.ACCCOMMANDSTART << 1) & 0xFF);
113        m.setElement(2, MarklinConstants.HASHBYTE1 & 0xFF);
114        m.setElement(3, MarklinConstants.HASHBYTE2 & 0xFF);
115        m.setElement(4, 0x06 & 0xFF); //five bytes;
116        m.setElement(MarklinConstants.CANADDRESSBYTE1, (addr >> 24) & 0xFF);
117        m.setElement(MarklinConstants.CANADDRESSBYTE2, (addr >> 16) & 0xFF);
118        m.setElement(MarklinConstants.CANADDRESSBYTE3, (addr >> 8) & 0xFF);
119        m.setElement(MarklinConstants.CANADDRESSBYTE4, (addr) & 0xFF);
120        m.setElement(9, state & 0xff);
121        m.setElement(10, power & 0xff);
122        return m;
123    }
124
125    public static MarklinMessage getQryLocoSpeed(int addr) {
126        MarklinMessage m = new MarklinMessage();
127        m.setElement(0, (MarklinConstants.LOCOSPEED >> 7) & 0xFF);
128        m.setElement(1, (MarklinConstants.LOCOSPEED << 1) & 0xFF);
129        m.setElement(2, MarklinConstants.HASHBYTE1 & 0xFF);
130        m.setElement(3, MarklinConstants.HASHBYTE2 & 0xFF);
131        m.setElement(4, 0x04 & 0xFF);
132        m.setElement(MarklinConstants.CANADDRESSBYTE1, (addr >> 24) & 0xFF);
133        m.setElement(MarklinConstants.CANADDRESSBYTE2, (addr >> 16) & 0xFF);
134        m.setElement(MarklinConstants.CANADDRESSBYTE3, (addr >> 8) & 0xFF);
135        m.setElement(MarklinConstants.CANADDRESSBYTE4, (addr) & 0xFF);
136        return m;
137    }
138
139    public static MarklinMessage setLocoSpeed(int addr, int speed) {
140        MarklinMessage m = new MarklinMessage();
141        m.setElement(0, (MarklinConstants.LOCOSPEED >> 7) & 0xFF);
142        m.setElement(1, (MarklinConstants.LOCOSPEED << 1) & 0xFF);
143        m.setElement(2, MarklinConstants.HASHBYTE1 & 0xFF);
144        m.setElement(3, MarklinConstants.HASHBYTE2 & 0xFF);
145        m.setElement(4, 0x06 & 0xFF);
146        m.setElement(MarklinConstants.CANADDRESSBYTE1, (addr >> 24) & 0xFF);
147        m.setElement(MarklinConstants.CANADDRESSBYTE2, (addr >> 16) & 0xFF);
148        m.setElement(MarklinConstants.CANADDRESSBYTE3, (addr >> 8) & 0xFF);
149        m.setElement(MarklinConstants.CANADDRESSBYTE4, (addr) & 0xFF);
150        m.setElement(9, (speed >> 8) & 0xff);
151        m.setElement(10, speed & 0xff);
152        return m;
153    }
154
155    public static MarklinMessage setLocoEmergencyStop(int addr) {
156        MarklinMessage m = new MarklinMessage();
157        m.setElement(0, MarklinConstants.SYSCOMMANDSTART & 0xFF);
158        m.setElement(1, 0x00 & 0xFF);
159        m.setElement(2, MarklinConstants.HASHBYTE1 & 0xFF);
160        m.setElement(3, MarklinConstants.HASHBYTE2 & 0xFF);
161        m.setElement(4, 0x05 & 0xFF); //five bytes;
162        m.setElement(MarklinConstants.CANADDRESSBYTE1, (addr >> 24) & 0xFF);
163        m.setElement(MarklinConstants.CANADDRESSBYTE2, (addr >> 16) & 0xFF);
164        m.setElement(MarklinConstants.CANADDRESSBYTE3, (addr >> 8) & 0xFF);
165        m.setElement(MarklinConstants.CANADDRESSBYTE4, (addr) & 0xFF);
166        m.setElement(9, MarklinConstants.LOCOEMERGENCYSTOP & 0xFF);
167        return m;
168    }
169
170    public static MarklinMessage setLocoSpeedSteps(int addr, int step) {
171        MarklinMessage m = new MarklinMessage();
172        m.setElement(0, MarklinConstants.SYSCOMMANDSTART & 0xFF);
173        m.setElement(1, 0x00 & 0xFF);
174        m.setElement(2, MarklinConstants.HASHBYTE1 & 0xFF);
175        m.setElement(3, MarklinConstants.HASHBYTE2 & 0xFF);
176        m.setElement(4, 0x05 & 0xFF); //five bytes;
177        m.setElement(MarklinConstants.CANADDRESSBYTE1, (addr >> 24) & 0xFF);
178        m.setElement(MarklinConstants.CANADDRESSBYTE2, (addr >> 16) & 0xFF);
179        m.setElement(MarklinConstants.CANADDRESSBYTE3, (addr >> 8) & 0xFF);
180        m.setElement(MarklinConstants.CANADDRESSBYTE4, (addr) & 0xFF);
181        m.setElement(9, 0x05 & 0xFF);
182        m.setElement(10, step & 0xFF);
183        return m;
184    }
185
186    public static MarklinMessage getQryLocoDirection(int addr) {
187        MarklinMessage m = new MarklinMessage();
188        m.setElement(0, (MarklinConstants.LOCODIRECTION >> 7) & 0xFF);
189        m.setElement(1, (MarklinConstants.LOCODIRECTION << 1) & 0xFF);
190        m.setElement(2, MarklinConstants.HASHBYTE1 & 0xFF);
191        m.setElement(3, MarklinConstants.HASHBYTE2 & 0xFF);
192        m.setElement(4, 0x04 & 0xFF);
193        m.setElement(MarklinConstants.CANADDRESSBYTE1, (addr >> 24) & 0xFF);
194        m.setElement(MarklinConstants.CANADDRESSBYTE2, (addr >> 16) & 0xFF);
195        m.setElement(MarklinConstants.CANADDRESSBYTE3, (addr >> 8) & 0xFF);
196        m.setElement(MarklinConstants.CANADDRESSBYTE4, (addr) & 0xFF);
197        return m;
198    }
199
200    public static MarklinMessage setLocoDirection(int addr, int dir) {
201        MarklinMessage m = new MarklinMessage();
202        m.setElement(0, (MarklinConstants.LOCODIRECTION >> 7) & 0xFF);
203        m.setElement(1, (MarklinConstants.LOCODIRECTION << 1) & 0xFF);
204        m.setElement(2, MarklinConstants.HASHBYTE1 & 0xFF);
205        m.setElement(3, MarklinConstants.HASHBYTE2 & 0xFF);
206        m.setElement(4, 0x05 & 0xFF);
207        m.setElement(MarklinConstants.CANADDRESSBYTE1, (addr >> 24) & 0xFF);
208        m.setElement(MarklinConstants.CANADDRESSBYTE2, (addr >> 16) & 0xFF);
209        m.setElement(MarklinConstants.CANADDRESSBYTE3, (addr >> 8) & 0xFF);
210        m.setElement(MarklinConstants.CANADDRESSBYTE4, (addr) & 0xFF);
211        m.setElement(9, dir & 0xff);
212        return m;
213    }
214
215    public static MarklinMessage getQryLocoFunction(int addr, int funct) {
216        MarklinMessage m = new MarklinMessage();
217        m.setElement(0, (MarklinConstants.LOCOFUNCTION >> 7) & 0xFF);
218        m.setElement(1, (MarklinConstants.LOCOFUNCTION << 1) & 0xFF);
219        m.setElement(2, MarklinConstants.HASHBYTE1 & 0xFF);
220        m.setElement(3, MarklinConstants.HASHBYTE2 & 0xFF);
221        m.setElement(4, 0x05 & 0xFF);
222        m.setElement(MarklinConstants.CANADDRESSBYTE1, (addr >> 24) & 0xFF);
223        m.setElement(MarklinConstants.CANADDRESSBYTE2, (addr >> 16) & 0xFF);
224        m.setElement(MarklinConstants.CANADDRESSBYTE3, (addr >> 8) & 0xFF);
225        m.setElement(MarklinConstants.CANADDRESSBYTE4, (addr) & 0xFF);
226        m.setElement(9, (funct) & 0xFF);
227        return m;
228    }
229
230    public static MarklinMessage setLocoFunction(int addr, int funct, int state) {
231        MarklinMessage m = new MarklinMessage();
232        m.setElement(0, (MarklinConstants.LOCOFUNCTION >> 7) & 0xFF);
233        m.setElement(1, (MarklinConstants.LOCOFUNCTION << 1) & 0xFF);
234        m.setElement(2, MarklinConstants.HASHBYTE1 & 0xFF);
235        m.setElement(3, MarklinConstants.HASHBYTE2 & 0xFF);
236        m.setElement(4, 0x06 & 0xFF);
237        m.setElement(MarklinConstants.CANADDRESSBYTE1, (addr >> 24) & 0xFF);
238        m.setElement(MarklinConstants.CANADDRESSBYTE2, (addr >> 16) & 0xFF);
239        m.setElement(MarklinConstants.CANADDRESSBYTE3, (addr >> 8) & 0xFF);
240        m.setElement(MarklinConstants.CANADDRESSBYTE4, (addr) & 0xFF);
241        m.setElement(9, funct & 0xff);
242        m.setElement(10, state & 0xff);
243        m.getAddress();
244        return m;
245    }
246
247    public static MarklinMessage sensorPollMessage(int module) {
248        MarklinMessage m = new MarklinMessage();
249        m.setElement(0, (MarklinConstants.FEECOMMANDSTART >> 7) & 0xFF);
250        m.setElement(1, (MarklinConstants.FEECOMMANDSTART << 1) & 0xFF);
251        m.setElement(2, MarklinConstants.HASHBYTE1 & 0xFF);
252        m.setElement(3, MarklinConstants.HASHBYTE2 & 0xFF);
253        m.setElement(4, 0x05 & 0xFF); //five bytes;
254        m.setElement(MarklinConstants.CANADDRESSBYTE1, (MY_UID >> 24) & 0xFF);
255        m.setElement(MarklinConstants.CANADDRESSBYTE2, (MY_UID >> 16) & 0xFF);
256        m.setElement(MarklinConstants.CANADDRESSBYTE3, (MY_UID >> 8) & 0xFF);
257        m.setElement(MarklinConstants.CANADDRESSBYTE4, (MY_UID) & 0xFF);
258        m.setElement(9, module & 0xFF);
259        return m;
260    }
261
262    public long getAddress() {
263        long addr = getElement(MarklinConstants.CANADDRESSBYTE1);
264        addr = (addr << 8) + getElement(MarklinConstants.CANADDRESSBYTE2);
265        addr = (addr << 8) + getElement(MarklinConstants.CANADDRESSBYTE3);
266        addr = (addr << 8) + getElement(MarklinConstants.CANADDRESSBYTE4);
267
268        return addr;
269    }
270
271    public static MarklinMessage getProgMode() {
272        return new MarklinMessage();
273    }
274
275    public static MarklinMessage getExitProgMode() {
276        return new MarklinMessage();
277    }
278
279    public static MarklinMessage getReadPagedCV(int cv) { //Rxxx
280        return new MarklinMessage();
281    }
282
283    public static MarklinMessage getWritePagedCV(int cv, int val) { //Pxxx xxx
284        return new MarklinMessage();
285    }
286
287    public static MarklinMessage getReadRegister(int reg) { //Vx
288        return new MarklinMessage();
289    }
290
291    public static MarklinMessage getWriteRegister(int reg, int val) { //Sx xxx
292        return new MarklinMessage();
293    }
294
295    public static MarklinMessage getReadDirectCV(int cv) { //Rxxx
296        return new MarklinMessage();
297    }
298
299    public static MarklinMessage getWriteDirectCV(int cv, int val) { //Pxxx xxx
300        return new MarklinMessage();
301    }
302}