001/*
002 * To change this license header, choose License Headers in Project Properties.
003 * To change this template file, choose Tools | Templates
004 * and open the template in the editor.
005 */
006package jmri.jmrix.openlcb;
007
008import jmri.Light;
009import jmri.LightControl;
010import jmri.implementation.AbstractLight;
011import org.openlcb.OlcbInterface;
012import org.openlcb.implementations.BitProducerConsumer;
013import org.openlcb.implementations.VersionedValueListener;
014import org.slf4j.Logger;
015import org.slf4j.LoggerFactory;
016
017import javax.annotation.Nonnull;
018
019/**
020 *
021 * @author jcollell
022 */
023public class OlcbLight extends AbstractLight {
024    
025    private static final int PC_DEFAULT_FLAGS = BitProducerConsumer.DEFAULT_FLAGS &
026            (~BitProducerConsumer.LISTEN_INVALID_STATE);
027    static final boolean DEFAULT_IS_AUTHORITATIVE = true;
028    static final boolean DEFAULT_LISTEN = true;
029    private boolean _finishedLoad = false;
030    
031    OlcbAddress addrOn;    // go to On state
032    OlcbAddress addrOff;  // go to Off state
033    OlcbInterface iface;
034    
035    VersionedValueListener<Boolean> lightListener;
036    BitProducerConsumer pc;
037    
038    /**
039     * Common initialization for both constructors.
040     * <p>
041     *
042     */
043    private void init(String address) {
044        // build local addresses
045        OlcbAddress a = new OlcbAddress(address);
046        OlcbAddress[] v = a.split();
047        if (v == null) {
048            log.error("Did not find usable system name: {}", address);
049            return;
050        }
051        if (v.length == 2) {
052            addrOn = v[0];
053            addrOff = v[1];
054        } else {
055            log.error("Can't parse OpenLCB Light system name: {}", address);
056        }
057    }
058    
059    
060    /**
061     * Helper function that will be invoked after construction once the properties have been
062     * loaded. Used specifically for preventing double initialization when loading lights from
063     * XML.
064     */
065    void finishLoad() {
066        int flags = PC_DEFAULT_FLAGS;
067        flags = OlcbUtils.overridePCFlagsFromProperties(this, flags);
068        pc = new BitProducerConsumer(iface, addrOn.toEventID(),
069                addrOff.toEventID(), flags);
070        lightListener = new VersionedValueListener<Boolean>(pc.getValue()) {
071            @Override
072            public void update(Boolean value) {
073                setState(value ? Light.ON : Light.OFF);
074            }
075        };
076        // A Light Control will have failed to set its state during xml load
077        // as the LightListener is not present, so we re-activate any Light Controls
078        activateLight();
079    }
080    
081    /**
082     * Activate a light activating all its LightControl objects.
083     */
084    @Override
085    public void activateLight() {
086        // during xml load any Light Controls may attempt to set the Light before the
087        // lightListener has been set
088        if (lightListener==null){
089            return;
090        }
091        lightControlList.stream().forEach(LightControl::activateLightControl);
092        mActive = true; // set flag for control listeners
093        _finishedLoad = true;
094    }
095    
096    /** {@inheritDoc} */
097    @Override
098    public void setState(int newState) {
099        if (_finishedLoad){
100            super.setState(newState);
101        }
102        else {
103            log.debug("Light {} status being set while still Activating",this);
104        }
105    }
106    
107    /**
108     * Set the current state of this Light This routine requests the hardware to
109     * change to newState.
110     * @param oldState old state
111     * @param newState new state
112     */
113    @Override
114    protected void doNewState(int oldState, int newState) {
115        switch (newState) {
116            case Light.ON:
117                lightListener.setFromOwnerWithForceNotify(true);
118                break;
119            case Light.OFF:
120                lightListener.setFromOwnerWithForceNotify(false);
121                break;
122            case Light.UNKNOWN:
123                if (pc != null) {
124                    pc.resetToDefault();
125                }   break;
126            default:
127                break;
128        }
129    }
130    
131    /** {@inheritDoc} */
132    @Override
133    public void setProperty(@Nonnull String key, Object value) {
134        Object old = getProperty(key);
135        super.setProperty(key, value);
136        if (value.equals(old)) return;
137        if (pc == null) return;
138        finishLoad();
139    }
140    
141    /** {@inheritDoc} */
142    @Override
143    public void dispose() {
144        if (lightListener != null) lightListener.release();
145        if (pc != null) pc.release();
146        super.dispose();
147    }
148    
149    private final static Logger log = LoggerFactory.getLogger(OlcbLight.class);
150
151    public OlcbLight(String systemName) {
152        super(systemName);
153    }
154    
155    public OlcbLight(String prefix, String address, OlcbInterface iface) {
156        super(prefix + "L" + address);
157        this.iface = iface;
158        init(address);
159    }
160}