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