001package jmri.jmrix.sprog;
002
003import java.util.Arrays;
004import jmri.DccLocoAddress;
005import jmri.SpeedStepMode;
006import jmri.NmraPacket;
007import org.slf4j.Logger;
008import org.slf4j.LoggerFactory;
009
010/**
011 * Represent information for a DCC Command Station Queue entry where each entry
012 * is a DCC packet to be transmitted to the rails.
013 * <p>
014 * A SlotListener can be registered to hear of changes in this slot. All changes
015 * in values will result in notification.
016 * <p>
017 * Updated by Andrew Crosland February 2012 to allow slots to hold 28 step speed
018 * packets
019 *
020 * @author Andrew Crosland Copyright (C) 2006, 2012
021 * @author Andrew Berridge 2010
022 */
023public class SprogSlot {
024
025    private boolean speedPacket = false;
026    private SpeedStepMode speedMode = SpeedStepMode.NMRA_DCC_128;
027
028    public SprogSlot(int num) {
029        payload = new byte[SprogConstants.MAX_PACKET_LENGTH];
030        payload[0] = 0;
031        payload[1] = 0;
032        payload[2] = 0;
033        f0to4Packet = false;
034        f5to8Packet = false;
035        f9to12Packet = false;
036        f13to20Packet = false;
037        f21to28Packet = false;
038        repeat = -1;
039        addr = 0;
040        isLong = false;
041        spd = 0;
042        forward = true;
043        status = SprogConstants.SLOT_FREE;
044        slot = num;
045        opsPkt = false;
046    }
047
048    private byte[] payload;
049    // repeat of -1 is a persistent entry, ie a loco slot
050    private int repeat;
051    private int addr;
052    private boolean isLong;
053    private int spd;
054    private boolean forward;
055    private int status;
056    private final int slot;
057    private boolean opsPkt;
058
059    private boolean f0to4Packet;
060    private boolean f5to8Packet;
061    private boolean f9to12Packet;
062    private boolean f13to20Packet;
063    private boolean f21to28Packet;
064
065    public boolean isF0to4Packet() {
066        return f0to4Packet;
067    }
068
069    public boolean isF5to8Packet() {
070        return f5to8Packet;
071    }
072
073    public boolean isF9to12Packet() {
074        return f9to12Packet;
075    }
076
077    public boolean isF13to20Packet() {
078        return f13to20Packet;
079    }
080
081    public boolean isF21to28Packet() {
082        return f21to28Packet;
083    }
084
085    private boolean repeatF0 = false;
086    private boolean repeatF1 = false;
087    private boolean repeatF2 = false;
088    private boolean repeatF3 = false;
089    private boolean repeatF4 = false;
090    private boolean repeatF5 = false;
091    private boolean repeatF6 = false;
092    private boolean repeatF7 = false;
093    private boolean repeatF8 = false;
094    private boolean repeatF9 = false;
095    private boolean repeatF10 = false;
096    private boolean repeatF11 = false;
097    private boolean repeatF12 = false;
098    private boolean repeatF13 = false;
099    private boolean repeatF14 = false;
100    private boolean repeatF15 = false;
101    private boolean repeatF16 = false;
102    private boolean repeatF17 = false;
103    private boolean repeatF18 = false;
104    private boolean repeatF19 = false;
105    private boolean repeatF20 = false;
106    private boolean repeatF21 = false;
107    private boolean repeatF22 = false;
108    private boolean repeatF23 = false;
109    private boolean repeatF24 = false;
110    private boolean repeatF25 = false;
111    private boolean repeatF26 = false;
112    private boolean repeatF27 = false;
113    private boolean repeatF28 = false;
114
115    /**
116     * Set the contents of the slot. Intended for accessory packets.
117     *
118     * @param address int
119     * @param payload byte[]
120     * @param repeat  int
121     */
122    public void set(int address, byte[] payload, int repeat) {
123        addr = address;
124
125        Arrays.copyOf(payload, payload.length);
126
127        this.setRepeat(repeat);
128        status = SprogConstants.SLOT_IN_USE;
129    }
130
131    public void setAccessoryPacket(int address, boolean closed, int repeats) {
132        this.payload = NmraPacket.accDecoderPkt(address, closed);
133        this.addr = address + 10000;
134        this.repeat = repeats;
135        status = SprogConstants.SLOT_IN_USE;
136    }
137
138    public boolean isSpeedPacket() {
139        return speedPacket;
140    }
141
142    public void setSpeed(SpeedStepMode mode, int address, boolean isLongAddress, int speed, boolean forward) {
143        addr = address;
144        isLong = isLongAddress;
145        spd = speed;
146        this.speedPacket = true;
147        this.speedMode = mode;
148        this.f0to4Packet = false;
149        this.f5to8Packet = false;
150        this.f9to12Packet = false;
151        this.f13to20Packet = false;
152        this.f21to28Packet = false;
153        this.forward = forward;
154        if (mode == SpeedStepMode.NMRA_DCC_28) {
155            this.payload = jmri.NmraPacket.speedStep28Packet(true, addr,
156                    isLong, spd, forward);
157        } else {
158            this.payload = jmri.NmraPacket.speedStep128Packet(addr,
159                    isLong, spd, forward);
160        }
161        status = SprogConstants.SLOT_IN_USE;
162    }
163
164    public void setOps(int address, boolean longAddr, int cv, int val) {
165        payload = NmraPacket.opsCvWriteByte(address, longAddr, cv, val);
166        this.repeat = SprogConstants.OPS_REPEATS;
167        this.opsPkt = true;
168        status = SprogConstants.SLOT_IN_USE;
169    }
170
171    public void f5to8packet(int address, boolean isLongAddress,
172            boolean f5, boolean f5Momentary,
173            boolean f6, boolean f6Momentary,
174            boolean f7, boolean f7Momentary,
175            boolean f8, boolean f8Momentary) {
176
177        this.f5to8Packet = true;
178        this.addr = address;
179        this.isLong = isLongAddress;
180
181        //Were we repeating any functions which we are now not?
182        if ((this.repeatF5 && !f5)
183                || (this.repeatF6 && !f6)
184                || (this.repeatF7 && !f7)
185                || (this.repeatF8 && !f8)) {
186            this.repeat = 3; //Then repeat 3 times
187        }
188
189        this.repeatF5 = !f5Momentary && f5;
190        this.repeatF6 = !f6Momentary && f6;
191        this.repeatF7 = !f7Momentary && f7;
192        this.repeatF8 = !f8Momentary && f8;
193
194        this.payload = jmri.NmraPacket.function5Through8Packet(address,
195                isLongAddress,
196                f5, f6, f7, f8);
197        this.status = SprogConstants.SLOT_IN_USE;
198
199    }
200
201    public void f9to12packet(int address, boolean isLongAddress,
202            boolean f9, boolean f9Momentary,
203            boolean f10, boolean f10Momentary,
204            boolean f11, boolean f11Momentary,
205            boolean f12, boolean f12Momentary) {
206
207        this.f9to12Packet = true;
208        this.addr = address;
209        this.isLong = isLongAddress;
210
211        //Were we repeating any functions which we are now not?
212        if ((this.repeatF9 && !f9)
213                || (this.repeatF10 && !f10)
214                || (this.repeatF11 && !f11)
215                || (this.repeatF12 && !f12)) {
216            this.repeat = 3; //Then repeat 3 times
217        }
218
219        this.repeatF9 = !f9Momentary && f9;
220        this.repeatF10 = !f10Momentary && f10;
221        this.repeatF11 = !f11Momentary && f11;
222        this.repeatF12 = !f12Momentary && f12;
223
224        this.payload = jmri.NmraPacket.function9Through12Packet(address,
225                isLongAddress,
226                f9, f10, f11, f12);
227        this.status = SprogConstants.SLOT_IN_USE;
228
229    }
230
231    public void f13to20packet(int address, boolean isLongAddress,
232            boolean f13, boolean f13Momentary,
233            boolean f14, boolean f14Momentary,
234            boolean f15, boolean f15Momentary,
235            boolean f16, boolean f16Momentary,
236            boolean f17, boolean f17Momentary,
237            boolean f18, boolean f18Momentary,
238            boolean f19, boolean f19Momentary,
239            boolean f20, boolean f20Momentary) {
240
241        this.f13to20Packet = true;
242        this.addr = address;
243        this.isLong = isLongAddress;
244
245        //Were we repeating any functions which we are now not?
246        if ((this.repeatF13 && !f13)
247                || (this.repeatF14 && !f14)
248                || (this.repeatF15 && !f15)
249                || (this.repeatF16 && !f16)
250                || (this.repeatF17 && !f17)
251                || (this.repeatF18 && !f18)
252                || (this.repeatF19 && !f19)
253                || (this.repeatF20 && !f20)) {
254            this.repeat = 3; //Then repeat 3 times
255        }
256
257        this.repeatF13 = !f13Momentary && f13;
258        this.repeatF14 = !f14Momentary && f14;
259        this.repeatF15 = !f15Momentary && f15;
260        this.repeatF16 = !f16Momentary && f16;
261        this.repeatF17 = !f17Momentary && f17;
262        this.repeatF18 = !f18Momentary && f18;
263        this.repeatF19 = !f19Momentary && f19;
264        this.repeatF20 = !f20Momentary && f20;
265
266        this.payload = jmri.NmraPacket.function13Through20Packet(address,
267                isLongAddress,
268                f13, f14, f15, f16,
269                f17, f18, f19, f20);
270        this.status = SprogConstants.SLOT_IN_USE;
271    }
272
273    public void f21to28packet(int address, boolean isLongAddress,
274            boolean f21, boolean f21Momentary,
275            boolean f22, boolean f22Momentary,
276            boolean f23, boolean f23Momentary,
277            boolean f24, boolean f24Momentary,
278            boolean f25, boolean f25Momentary,
279            boolean f26, boolean f26Momentary,
280            boolean f27, boolean f27Momentary,
281            boolean f28, boolean f28Momentary) {
282
283        this.f21to28Packet = true;
284        this.addr = address;
285        this.isLong = isLongAddress;
286
287        //Were we repeating any functions which we are now not?
288        if ((this.repeatF21 && !f21)
289                || (this.repeatF22 && !f22)
290                || (this.repeatF23 && !f23)
291                || (this.repeatF24 && !f24)
292                || (this.repeatF25 && !f25)
293                || (this.repeatF26 && !f26)
294                || (this.repeatF27 && !f27)
295                || (this.repeatF28 && !f28)) {
296            this.repeat = 3; //Then repeat 3 times
297        }
298
299        this.repeatF21 = !f21Momentary && f21;
300        this.repeatF22 = !f22Momentary && f22;
301        this.repeatF23 = !f23Momentary && f23;
302        this.repeatF24 = !f24Momentary && f24;
303        this.repeatF25 = !f25Momentary && f25;
304        this.repeatF26 = !f26Momentary && f26;
305        this.repeatF27 = !f27Momentary && f27;
306        this.repeatF28 = !f28Momentary && f28;
307
308        this.payload = jmri.NmraPacket.function21Through28Packet(address,
309                isLongAddress,
310                f21, f22, f23, f24,
311                f25, f26, f27, f28);
312        this.status = SprogConstants.SLOT_IN_USE;
313    }
314
315    public void f0to4packet(int address, boolean isLongAddress,
316            boolean f0, boolean f0Momentary,
317            boolean f1, boolean f1Momentary,
318            boolean f2, boolean f2Momentary,
319            boolean f3, boolean f3Momentary,
320            boolean f4, boolean f4Momentary) {
321
322        this.f0to4Packet = true;
323        this.addr = address;
324        this.isLong = isLongAddress;
325
326        //Were we repeating any functions which we are now not?
327        if ((this.repeatF0 && !f0)
328                || (this.repeatF1 && !f1)
329                || (this.repeatF2 && !f2)
330                || (this.repeatF3 && !f3)
331                || (this.repeatF4 && !f4)) {
332            this.repeat = 3; //Then repeat 3 times
333        }
334
335        this.repeatF0 = !f0Momentary && f0;
336        this.repeatF1 = !f1Momentary && f1;
337        this.repeatF2 = !f2Momentary && f2;
338        this.repeatF3 = !f3Momentary && f3;
339        this.repeatF4 = !f4Momentary && f4;
340        
341        this.payload = jmri.NmraPacket.function0Through4Packet(address,
342                isLongAddress,
343                f0, f1, f2, f3, f4);
344        this.status = SprogConstants.SLOT_IN_USE;
345
346    }
347
348    public boolean isFinished() {
349        if (this.isF0to4Packet()) {
350            if ((this.repeatF0 || this.repeatF1 || this.repeatF2 || this.repeatF3 || this.repeatF4)) {
351                return false;
352            }
353        }
354        if (this.isF5to8Packet()) {
355            if ((this.repeatF5 || this.repeatF6 || this.repeatF7 || this.repeatF8)) {
356                return false;
357            }
358        }
359        if (this.isF9to12Packet()) {
360            if ((this.repeatF9 || this.repeatF10 || this.repeatF11 || this.repeatF12)) {
361                return false;
362            }
363        }
364        if (this.isF13to20Packet()) {
365            if ((this.repeatF13 || this.repeatF14 || this.repeatF15 || this.repeatF16)
366                    || (this.repeatF17 || this.repeatF18 || this.repeatF19 || this.repeatF20)) {
367                return false;
368            }
369        }
370        if (this.isF21to28Packet()) {
371            if ((this.repeatF21 || this.repeatF22 || this.repeatF23 || this.repeatF24)
372                    || (this.repeatF25 || this.repeatF26 || this.repeatF27 || this.repeatF28)) {
373                return false;
374            }
375        }
376        if (this.isSpeedPacket() && this.status == SprogConstants.SLOT_IN_USE) {
377            return false;
378        }
379        if (this.repeat > 0 && this.status == SprogConstants.SLOT_IN_USE) {
380            return false;
381        }
382        /* Finished - clear and return true */
383        this.clear();
384        return true;
385    }
386
387    public void eStop() {
388        this.setSpeed(this.speedMode, this.addr, this.isLong, 1, this.forward);
389    }
390
391    // Access methods
392
393    public void clear() {
394        status = SprogConstants.SLOT_FREE;
395        addr = 0;
396        spd = 0;
397        speedPacket = false;
398        f0to4Packet = false;
399        f5to8Packet = false;
400        f9to12Packet = false;
401        f13to20Packet = false;
402        f21to28Packet = false;
403        if (payload != null) {
404            payload[0] = 0;
405            payload[1] = 0;
406            payload[2] = 0;
407        }
408        opsPkt = false;
409    }
410
411    public boolean isLongAddress() {
412        return isLong;
413    }
414
415    public boolean isFree() {
416        return (status == SprogConstants.SLOT_FREE);
417    }
418
419    public int slotStatus() {
420        return status;
421    }
422
423    public int getRepeat() {
424        return repeat;
425    }
426
427    public void setRepeat(int r) {
428        repeat = r;
429    }
430
431    private int doRepeat() {
432        if (repeat > 0) {
433            log.debug("Slot {} repeats", slot);
434            repeat--;
435            if (repeat == 0) {
436                log.debug("Clear slot {} due to repeats exhausted", slot);
437                this.clear();
438            }
439        }
440        return repeat;
441    }
442
443    public int speed() {
444        return spd;
445    }
446
447    public int locoAddr() {
448        return addr;
449    }
450
451    public int getAddr() {
452        if (opsPkt == false) {
453            return addr;
454        } else {
455            return addressFromPacket();
456        }
457    }
458
459    public void setAddr(int a) {
460        addr = a;
461    }
462
463    public boolean getIsLong() {
464        if (opsPkt == false) {
465            return isLong;
466        } else {
467            return ((payload[0] & 0xC0) >= 0xC0);
468        }
469    }
470
471    public void setIsLong(boolean a) {
472        isLong = a;
473    }
474
475    public boolean isForward() {
476        return forward;
477    }
478
479    public boolean isOpsPkt() {
480        return opsPkt;
481    }
482
483    public boolean isActiveAddressMatch(DccLocoAddress address) {
484        return ( status == SprogConstants.SLOT_IN_USE && getAddr() == address.getNumber() && getIsLong() == address.isLongAddress() );
485    }
486
487    /**
488     * Get the payload of this slot. Note - if this slot has a number of
489     * repeats, calling this method will also decrement the internal repeat
490     * counter.
491     *
492     * @return a byte array containing the payload of this slot
493     */
494    public byte[] getPayload() {
495
496        byte[] p;
497        if (payload != null) {
498            p = Arrays.copyOf(payload, getPayloadLength());//, a Java 1.6 construct
499        } else {
500            p = new byte[0];
501        }
502        /*byte [] p = new byte[getPayloadLength()];
503         for (int i = 0; i<getPayloadLength(); i++) p[i] = payload[i];*/
504
505        //decrement repeat counter if appropriate
506        doRepeat();
507        return p;
508
509    }
510
511    public int getSlotNumber() {
512        return slot;
513    }
514
515    private int getPayloadLength() {
516        return this.payload.length;
517    }
518
519    private long lastUpdateTime; // Time of last update for detecting stale slots
520
521    public long getLastUpdateTime() {
522        return lastUpdateTime;
523    }
524
525    /**
526     * Get the address from the packet.
527     *
528     * @return int address from payload
529     */
530    private int addressFromPacket() {
531        if (isFree()) {
532            return -1;
533        }
534        // First deal with possible extended address
535        if ((payload[0] & 0xC0) == 0xC0) {
536            return ((payload[0] & 0x3F) << 8 | (payload[1] & 0xFF));
537        }
538        return payload[0];
539    }
540
541    private final static Logger log = LoggerFactory.getLogger(SprogSlot.class);
542}