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}