001package jmri.jmrix.can.cbus;
002
003import jmri.Disposable;
004import jmri.JmriException;
005import jmri.jmrix.can.CanListener;
006import jmri.jmrix.can.CanMessage;
007import jmri.jmrix.can.CanReply;
008import jmri.jmrix.can.CanSystemConnectionMemo;
009import jmri.jmrix.can.TrafficController;
010import jmri.managers.AbstractPowerManager;
011
012/**
013 * PowerManager implementation for controlling CBUS layout power.
014 *
015 * @author Bob Jacobsen Copyright (C) 2001
016 * @author Andrew Crosland Copyright (C) 2009, 2021
017 */
018public class CbusPowerManager extends AbstractPowerManager<CanSystemConnectionMemo> implements CanListener, Disposable {
019
020    private TrafficController tc;
021
022    public CbusPowerManager(CanSystemConnectionMemo memo) {
023        super(memo);
024        // connect to the TrafficManager
025        tc = memo.getTrafficController();
026        addTc(tc);
027    }
028
029    @Override
030    public void setPower(int v) throws JmriException {
031        int old = power;
032        power = UNKNOWN; // while waiting for reply
033        checkTC();
034        if (v == ON) {
035            // send "Enable main track"
036            tc.sendCanMessage(CbusMessage.getRequestTrackOn(tc.getCanid()), this);
037        } else if (v == OFF) {
038            // send "Kill main track"
039            tc.sendCanMessage(CbusMessage.getRequestTrackOff(tc.getCanid()), this);
040        }
041        firePowerPropertyChange(old, power);
042    }
043
044    /**
045     * Notification to JMRI of main track power state. Does not send to Layout.
046     * Only used to bypass having the PowerManager respond to messages from the
047     * command station because I don't know why the PowerManager should not do
048     * the job the PowerManager API was created to do in the CBus package.
049     *
050     * @param newPower New Power Status
051     */
052    public void updatePower(int newPower) {
053        int oldPower = power;
054        if (oldPower != newPower) {
055            power = newPower;
056            firePowerPropertyChange(oldPower, power);
057        }
058    }
059
060    /**
061     * {@inheritDoc}
062     */
063    @Override
064    public void dispose() {
065        removeTc(tc);
066        tc = null;
067    }
068
069    private void checkTC() throws JmriException {
070        if (tc == null) {
071            throw new JmriException("attempt to use CbusPowerManager after dispose");
072        }
073    }
074
075    // to listen for status changes from Cbus system
076    @Override
077    public void reply(CanReply m) {
078        if (m.extendedOrRtr()) {
079            return;
080        }
081        int old = power;
082        if (CbusMessage.isTrackOff(m)) {
083            power = OFF;
084        } else if (CbusMessage.isTrackOn(m)) {
085            power = ON;
086        } else if (CbusMessage.isArst(m)) {
087            // Some CBUS command stations (e.g. CANCMD) will turn on the track
088            // power at start up, before sending ARST (System reset). Others,
089            // e.g., SPROG CBUS hardware can selectively turn on the track power
090            // so we selectively check for ARST here, based on connection settings.
091            if (memo.powerOnArst()) {
092                power = ON;
093            }
094        }
095        firePowerPropertyChange(old, power);
096    }
097
098    /**
099     * Does not listen to outgoing messages. {@inheritDoc}
100     */
101    @Override
102    public void message(CanMessage m) {
103        // do nothing
104    }
105
106}