001package jmri.jmrix.easydcc;
002
003import jmri.Consist;
004import jmri.ConsistListener;
005import jmri.DccLocoAddress;
006
007/**
008 * This is the Consist definition for a consist on an EasyDCC system. it uses
009 * the EasyDcc specific commands to build a consist.
010 *
011 * @author Paul Bender Copyright (C) 2006
012 */
013public class EasyDccConsist extends jmri.implementation.DccConsist {
014
015    private final EasyDccSystemConnectionMemo _memo;
016
017    // Initialize a consist for the specific address.
018    // The Default consist type is an advanced consist
019    public EasyDccConsist(int address, EasyDccSystemConnectionMemo memo) {
020        super(address);
021        _memo = memo;
022    }
023
024    // Initialize a consist for the specific address.
025    // The Default consist type is an advanced consist
026    public EasyDccConsist(DccLocoAddress address, EasyDccSystemConnectionMemo memo) {
027        super(address);
028        _memo = memo;
029    }
030
031    // Set the Consist Type.
032    @Override
033    public void setConsistType(int consist_type) {
034        switch (consist_type) {
035            case Consist.ADVANCED_CONSIST:
036            case Consist.CS_CONSIST:
037                consistType = consist_type;
038                break;
039            default:
040                log.error("Consist Type Not Supported");
041                notifyConsistListeners(new DccLocoAddress(0, false), ConsistListener.NotImplemented);
042                break;
043        }
044    }
045
046    /**
047     * Is this address allowed?
048     * On EasyDCC systems, all addresses but 0 can be used in a consist
049     * (either an Advanced Consist or a Standard Consist).
050     * {@inheritDoc}
051     */
052    @Override
053    public boolean isAddressAllowed(DccLocoAddress address) {
054        return address.getNumber() != 0;
055    }
056
057    /**
058     * Is there a size limit for this consist?
059     *
060     * @return 8 for EasyDcc Standard Consist,
061     * -1 for Decoder Assisted Consists (no limit),
062     * 0 for any other consist type
063     * {@inheritDoc}
064     */
065    @Override
066    public int sizeLimit() {
067        switch (consistType) {
068            case ADVANCED_CONSIST:
069                return -1;
070            case CS_CONSIST:
071                return 8;
072            default:
073                return 0;
074        }
075    }
076
077    /**
078     * Does the consist contain the specified address?
079     * {@inheritDoc}
080     */
081    @Override
082    public boolean contains(DccLocoAddress address) {
083        if (consistType == ADVANCED_CONSIST || consistType == CS_CONSIST) {
084            return consistList.contains(address);
085        } else {
086            log.error("Consist Type Not Supported");
087            notifyConsistListeners(address, ConsistListener.NotImplemented);
088        }
089        return false;
090    }
091
092    /**
093     * Get the relative direction setting for a specific
094     * locomotive in the consist.
095     * {@inheritDoc}
096     */
097    @Override
098    public boolean getLocoDirection(DccLocoAddress address) {
099        if (consistType == ADVANCED_CONSIST || consistType == CS_CONSIST) {
100            return consistDir.getOrDefault(address, false);
101        } else {
102            log.error("Consist Type Not Supported");
103            notifyConsistListeners(address, ConsistListener.NotImplemented);
104        }
105        return false;
106    }
107
108    /**
109     * Add an Address to the internal consist list object.
110     */
111    private synchronized void addToConsistList(DccLocoAddress locoAddress, boolean directionNormal) {
112        if (!(consistList.contains(locoAddress))) {
113            consistList.add(locoAddress);
114        }
115        consistDir.put(locoAddress, directionNormal);
116        if (consistType == CS_CONSIST && consistList.size() == 8) {
117            notifyConsistListeners(locoAddress,
118                    ConsistListener.OPERATION_SUCCESS
119                    | ConsistListener.CONSIST_FULL);
120        } else {
121            notifyConsistListeners(locoAddress,
122                    ConsistListener.OPERATION_SUCCESS);
123        }
124    }
125
126    /**
127     * Remove an address from the internal consist list object.
128     */
129    private synchronized void removeFromConsistList(DccLocoAddress locoAddress) {
130        consistDir.remove(locoAddress);
131        consistList.remove(locoAddress);
132        notifyConsistListeners(locoAddress, ConsistListener.OPERATION_SUCCESS);
133    }
134
135    /**
136     * Add a Locomotive to a Consist.
137     *
138     * @param locoAddress is the Locomotive address to add to the locomotive
139     * @param directionNormal is True if the locomotive is traveling
140     *        the same direction as the consist, or false otherwise.
141     */
142    @Override
143    public synchronized void add(DccLocoAddress locoAddress, boolean directionNormal) {
144        if (consistType == ADVANCED_CONSIST) {
145            addToConsistList(locoAddress, directionNormal);
146            addToAdvancedConsist(locoAddress, directionNormal);
147            //set the value in the roster entry for CV19
148            setRosterEntryCVValue(locoAddress);
149        } else if (consistType == CS_CONSIST) {
150            if (consistList.size() < 8) {
151                addToConsistList(locoAddress, directionNormal);
152                addToCSConsist(locoAddress, directionNormal);
153            } else {
154                notifyConsistListeners(locoAddress,
155                        ConsistListener.CONSIST_ERROR
156                        | ConsistListener.CONSIST_FULL);
157            }
158        } else {
159            log.error("Consist Type Not Supported");
160            notifyConsistListeners(locoAddress, ConsistListener.NotImplemented);
161        }
162    }
163
164    /**
165     * Restore a Locomotive to an Advanced Consist, but don't write to
166     * the command station.  This is used for restoring the consist
167     * from a file or adding a consist read from the command station.
168     *
169     * @param locoAddress is the Locomotive address to add to the locomotive
170     * @param directionNormal is True if the locomotive is traveling
171     *        the same direction as the consist, or false otherwise.
172     */
173    @Override
174    public synchronized void restore(DccLocoAddress locoAddress, boolean directionNormal) {
175        switch (consistType) {
176            case ADVANCED_CONSIST:
177            case CS_CONSIST:
178                addToConsistList(locoAddress, directionNormal);
179                break;
180            default:
181                log.error("Consist Type Not Supported");
182                notifyConsistListeners(locoAddress, ConsistListener.NotImplemented);
183                break;
184        }
185    }
186
187    /**
188     * Remove a Locomotive from this Consist.
189     *
190     * @param locoAddress is the Locomotive address to add to the locomotive
191     */
192    @Override
193    public synchronized void remove(DccLocoAddress locoAddress) {
194        switch (consistType) {
195            case ADVANCED_CONSIST:
196                //reset the value in the roster entry for CV19
197                resetRosterEntryCVValue(locoAddress);
198                removeFromAdvancedConsist(locoAddress);
199                removeFromConsistList(locoAddress);
200                break;
201            case CS_CONSIST:
202                removeFromCSConsist(locoAddress);
203                removeFromConsistList(locoAddress);
204                break;
205            default:
206                log.error("Consist Type Not Supported");
207                notifyConsistListeners(locoAddress, ConsistListener.NotImplemented);
208                break;
209        }
210    }
211
212    /**
213     * Add a Locomotive to an Advanced Consist.
214     *
215     * @param locoAddress is the Locomotive address to add to the locomotive
216     * @param directionNormal is True if the locomotive is traveling
217     *        the same direction as the consist, or false otherwise.
218     */
219    @Override
220    protected synchronized void addToAdvancedConsist(DccLocoAddress locoAddress, boolean directionNormal) {
221        log.debug("Add Locomotive {} to advanced consist {} With Direction Normal {}.", 
222            locoAddress, consistAddress, directionNormal);
223        // create the message and fill it,
224        byte[] contents = jmri.NmraPacket.consistControl(locoAddress.getNumber(),
225                locoAddress.isLongAddress(),
226                consistAddress.getNumber(),
227                directionNormal);
228        EasyDccMessage msg = new EasyDccMessage(4 + 3 * contents.length);
229        msg.setOpCode('S');
230        msg.setElement(1, ' ');
231        msg.setElement(2, '0');
232        msg.setElement(3, '5');
233        int j = 4;
234        for (int i = 0; i < contents.length; i++) {
235            j++;
236            msg.setElement(j, ' ');
237            msg.addIntAsTwoHex(contents[i] & 0xFF, j);
238            j += 2;
239        }
240
241        // send it
242        _memo.getTrafficController().sendEasyDccMessage(msg, null);
243    }
244
245    /**
246     * Remove a Locomotive from an Advanced Consist
247     *
248     * @param locoAddress is the Locomotive address to add to the locomotive
249     */
250    @Override
251    protected synchronized void removeFromAdvancedConsist(DccLocoAddress locoAddress) {
252        log.debug(" Remove Locomotive {} from advanced consist {}", locoAddress, consistAddress);
253        // create the message and fill it,
254        byte[] contents = jmri.NmraPacket.consistControl(locoAddress.getNumber(),
255                locoAddress.isLongAddress(),
256                0, true);
257        EasyDccMessage msg = new EasyDccMessage(4 + 3 * contents.length);
258        msg.setOpCode('S');
259        msg.setElement(1, ' ');
260        msg.setElement(2, '0');
261        msg.setElement(3, '5');
262        int j = 4;
263        for (int i = 0; i < contents.length; i++) {
264            j++;
265            msg.setElement(j, ' ');
266            msg.addIntAsTwoHex(contents[i] & 0xFF, j);
267            j += 2;
268        }
269
270        // send it
271        _memo.getTrafficController().sendEasyDccMessage(msg, null);
272    }
273
274    /**
275     * Add a Locomotive to an EasyDCC Standard Consist.
276     *
277     * @param locoAddress is the Locomotive address to add to the locomotive
278     * @param directionNormal is True if the locomotive is traveling
279     *        the same direction as the consist, or false otherwise.
280     */
281    private synchronized void addToCSConsist(DccLocoAddress locoAddress, boolean directionNormal) {
282        log.debug("Add Locomotive {} to Standard Consist {} With Direction Normal {}.", 
283            locoAddress, consistAddress, directionNormal);
284        EasyDccMessage m;
285        if (directionNormal) {
286            m = EasyDccMessage.getAddConsistNormal(consistAddress.getNumber(), locoAddress);
287        } else {
288            m = EasyDccMessage.getAddConsistReverse(consistAddress.getNumber(), locoAddress);
289        }
290        _memo.getTrafficController().sendEasyDccMessage(m, null);
291    }
292
293    /**
294     * Remove a Locomotive from an EasyDCC Standard Consist.
295     *
296     * @param LocoAddress is the Locomotive address to add to the locomotive
297     */
298    public synchronized void removeFromCSConsist(DccLocoAddress LocoAddress) {
299        log.debug("Remove Locomotive {} from Standard Consist {}.", LocoAddress, consistAddress);
300        EasyDccMessage m = EasyDccMessage.getSubtractConsist(consistAddress.getNumber(), LocoAddress);
301        _memo.getTrafficController().sendEasyDccMessage(m, null);
302    }
303
304    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(EasyDccConsist.class);
305
306}