001/**
002 * Consist Manager for use with the LocoNetConsist class for the
003 * consists it builds.
004 *
005 * @author Paul Bender Copyright (C) 2011
006 */
007package jmri.jmrix.loconet;
008
009import jmri.Consist;
010import jmri.LocoAddress;
011import jmri.DccLocoAddress;
012import jmri.implementation.AbstractConsistManager;
013import org.slf4j.Logger;
014import org.slf4j.LoggerFactory;
015
016public class LocoNetConsistManager extends AbstractConsistManager {
017
018    private LocoNetSystemConnectionMemo memo = null;
019    private boolean requestingUpdate = false;
020
021    /**
022     * Constructor - call the constructor for the superclass, and initialize the
023     * consist reader thread, which retrieves consist information from the
024     * command station
025     *
026     * @param lm the LocoNetSystemConnectionMemo to which this object is related
027     */
028    public LocoNetConsistManager(LocoNetSystemConnectionMemo lm) {
029        super();
030        this.memo = lm;
031    }
032
033    /**
034     * This implementation does support command station assisted consists, so
035     * return true.
036     *
037     */
038    @Override
039    public boolean isCommandStationConsistPossible() {
040        return true;
041    }
042
043    /**
044     * Does a CS consist require a separate consist address?
045     *
046     */
047    @Override
048    public boolean csConsistNeedsSeperateAddress() {
049        return false;
050    }
051
052    /**
053     * Add a new LocoNetConsist with the given address to
054     * consistTable/consistList
055     */
056    @Override
057    public Consist addConsist(LocoAddress address) {
058        if (! (address instanceof DccLocoAddress)) {
059            throw new IllegalArgumentException("address is not a DccLocoAddress object");
060        }
061        if (consistTable.containsKey(address)) // no duplicates allowed.
062        {
063            return consistTable.get(address);
064        }
065        LocoNetConsist consist;
066        consist = new LocoNetConsist((DccLocoAddress) address, memo);
067        consistTable.put(address, consist);
068        return consist;
069    }
070
071    /* request an update from the layout, loading
072     * Consists from the command station.
073     *
074     * On a LocoNet command station, the consists are stored in the
075     * slots in an array based tree.  Each node in a consist contains
076     * a pointer to the "top" slot in the consist.  A top slot is
077     * allowed to be a member of another consist.  When this occurs,
078     * it is labeled as a "mid" locomotive.
079     *
080     * This function updates the list of consists by scanning the
081     * slots and adding new "top" slot addresses and removing address
082     * that are no longer "top" locomotives.
083     */
084    @Override
085    public void requestUpdateFromLayout() {
086        if (!shouldRequestUpdateFromLayout()) {
087            return;
088        }
089        requestingUpdate = true;
090        SlotManager sm = memo.getSlotManager();
091
092        // in the first pass, check for consists top addresses in the
093        // command station slots.
094        for (int i = 0; i < 128; i++) {
095            LocoNetSlot s = sm.slot(i);
096            DccLocoAddress address = new DccLocoAddress(s.locoAddr(), LnThrottleManager.isLongAddress(s.locoAddr()));
097            if (log.isDebugEnabled()) {
098                log.debug(" Slot {} Address {} consist status {}", i, address, LnConstants.CONSIST_STAT(s.consistStatus()));
099            }
100            if (s.consistStatus() == LnConstants.CONSIST_TOP || s.consistStatus() == LnConstants.CONSIST_MID) {
101                // this is a consist top, add it to the list, if it is not there
102                // already.
103                if (!consistTable.containsKey(address)) {
104                    if (log.isDebugEnabled()) {
105                        log.debug("Adding Consist with Address {} due to command station read", address);
106                    }
107                    addConsist(address);
108                    getConsist(address).add(address, true); // add the address to the consist.
109                }
110            }
111        }
112
113        // make a second pass, this time looking for locomotives in a consist.
114        for (int i = 0; i < 128; i++) {
115            LocoNetSlot s = sm.slot(i);
116            DccLocoAddress address = new DccLocoAddress(s.locoAddr(), LnThrottleManager.isLongAddress(s.locoAddr()));
117            if (log.isDebugEnabled()) {
118                log.debug(" Slot {} Address {} consist status {}", i, address, LnConstants.CONSIST_STAT(s.consistStatus()));
119            }
120            if (s.consistStatus() == LnConstants.CONSIST_SUB || s.consistStatus() == LnConstants.CONSIST_MID) {
121                // this is a consist member, add it to the consist in the
122                // slot which it has a pointer to (the slot pointer is stored in
123                // the slot's speed).
124                DccLocoAddress lead = new DccLocoAddress(sm.slot(s.speed()).locoAddr(), LnThrottleManager.isLongAddress(sm.slot(s.speed()).locoAddr()));
125                getConsist(lead).add(address, s.isForward() == sm.slot(s.speed()).isForward());
126            }
127        }
128        requestingUpdate = false;
129    }
130
131    @Override
132    protected boolean shouldRequestUpdateFromLayout() {
133        return !requestingUpdate;
134    }
135    private final static Logger log = LoggerFactory.getLogger(LocoNetConsistManager.class);
136}