001package jmri.jmrit.logixng.expressions; 002 003import java.beans.*; 004import java.util.*; 005 006import javax.annotation.Nonnull; 007 008import jmri.*; 009import jmri.jmrit.logixng.*; 010import jmri.jmrit.logixng.util.LogixNG_SelectNamedBean; 011import jmri.jmrit.logixng.util.ReferenceUtil; 012import jmri.jmrit.logixng.util.parser.*; 013import jmri.jmrit.logixng.util.parser.ExpressionNode; 014import jmri.jmrit.logixng.util.parser.RecursiveDescentParser; 015import jmri.util.TypeConversionUtil; 016 017/** 018 * This expression sets the state of a light. 019 * 020 * @author Daniel Bergqvist Copyright 2018 021 */ 022public class ExpressionLight extends AbstractDigitalExpression 023 implements PropertyChangeListener { 024 025 private final LogixNG_SelectNamedBean<Light> _selectNamedBean = 026 new LogixNG_SelectNamedBean<>( 027 this, Light.class, InstanceManager.getDefault(LightManager.class)); 028 private Is_IsNot_Enum _is_IsNot = Is_IsNot_Enum.Is; 029 private NamedBeanAddressing _stateAddressing = NamedBeanAddressing.Direct; 030 private LightState _lightState = LightState.On; 031 private String _stateReference = ""; 032 private String _stateLocalVariable = ""; 033 private String _stateFormula = ""; 034 private ExpressionNode _stateExpressionNode; 035 036 public ExpressionLight(String sys, String user) 037 throws BadUserNameException, BadSystemNameException { 038 super(sys, user); 039 } 040 041 @Override 042 public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws ParserException { 043 DigitalExpressionManager manager = InstanceManager.getDefault(DigitalExpressionManager.class); 044 String sysName = systemNames.get(getSystemName()); 045 String userName = userNames.get(getSystemName()); 046 if (sysName == null) sysName = manager.getAutoSystemName(); 047 ExpressionLight copy = new ExpressionLight(sysName, userName); 048 copy.setComment(getComment()); 049 _selectNamedBean.copy(copy._selectNamedBean); 050 copy.setBeanState(_lightState); 051 copy.set_Is_IsNot(_is_IsNot); 052 copy.setStateAddressing(_stateAddressing); 053 copy.setStateFormula(_stateFormula); 054 copy.setStateLocalVariable(_stateLocalVariable); 055 copy.setStateReference(_stateReference); 056 return manager.registerExpression(copy); 057 } 058 059 public LogixNG_SelectNamedBean<Light> getSelectNamedBean() { 060 return _selectNamedBean; 061 } 062 063 public void set_Is_IsNot(Is_IsNot_Enum is_IsNot) { 064 _is_IsNot = is_IsNot; 065 } 066 067 public Is_IsNot_Enum get_Is_IsNot() { 068 return _is_IsNot; 069 } 070 071 public void setStateAddressing(NamedBeanAddressing addressing) throws ParserException { 072 _stateAddressing = addressing; 073 parseStateFormula(); 074 } 075 076 public NamedBeanAddressing getStateAddressing() { 077 return _stateAddressing; 078 } 079 080 public void setBeanState(LightState state) { 081 _lightState = state; 082 } 083 084 public LightState getBeanState() { 085 return _lightState; 086 } 087 088 public void setStateReference(@Nonnull String reference) { 089 if ((! reference.isEmpty()) && (! ReferenceUtil.isReference(reference))) { 090 throw new IllegalArgumentException("The reference \"" + reference + "\" is not a valid reference"); 091 } 092 _stateReference = reference; 093 } 094 095 public String getStateReference() { 096 return _stateReference; 097 } 098 099 public void setStateLocalVariable(@Nonnull String localVariable) { 100 _stateLocalVariable = localVariable; 101 } 102 103 public String getStateLocalVariable() { 104 return _stateLocalVariable; 105 } 106 107 public void setStateFormula(@Nonnull String formula) throws ParserException { 108 _stateFormula = formula; 109 parseStateFormula(); 110 } 111 112 public String getStateFormula() { 113 return _stateFormula; 114 } 115 116 private void parseStateFormula() throws ParserException { 117 if (_stateAddressing == NamedBeanAddressing.Formula) { 118 Map<String, Variable> variables = new HashMap<>(); 119 120 RecursiveDescentParser parser = new RecursiveDescentParser(variables); 121 _stateExpressionNode = parser.parseExpression(_stateFormula); 122 } else { 123 _stateExpressionNode = null; 124 } 125 } 126 127 /** {@inheritDoc} */ 128 @Override 129 public LogixNG_Category getCategory() { 130 return LogixNG_Category.ITEM; 131 } 132 133 private String getNewState() throws JmriException { 134 135 switch (_stateAddressing) { 136 case Reference: 137 return ReferenceUtil.getReference( 138 getConditionalNG().getSymbolTable(), _stateReference); 139 140 case LocalVariable: 141 SymbolTable symbolTable = getConditionalNG().getSymbolTable(); 142 return TypeConversionUtil 143 .convertToString(symbolTable.getValue(_stateLocalVariable), false); 144 145 case Formula: 146 return _stateExpressionNode != null 147 ? TypeConversionUtil.convertToString( 148 _stateExpressionNode.calculate( 149 getConditionalNG().getSymbolTable()), false) 150 : null; 151 152 default: 153 throw new IllegalArgumentException("invalid _addressing state: " + _stateAddressing.name()); 154 } 155 } 156 157 /** {@inheritDoc} */ 158 @Override 159 public boolean evaluate() throws JmriException { 160 Light light = _selectNamedBean.evaluateNamedBean(getConditionalNG()); 161 162 if (light == null) return false; 163 164 LightState checkLightState; 165 166 if ((_stateAddressing == NamedBeanAddressing.Direct)) { 167 checkLightState = _lightState; 168 } else { 169 checkLightState = LightState.valueOf(getNewState()); 170 } 171 172 LightState currentLightState = LightState.get(light.getKnownState()); 173 if (_is_IsNot == Is_IsNot_Enum.Is) { 174 return currentLightState == checkLightState; 175 } else { 176 return currentLightState != checkLightState; 177 } 178 } 179 180 @Override 181 public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException { 182 throw new UnsupportedOperationException("Not supported."); 183 } 184 185 @Override 186 public int getChildCount() { 187 return 0; 188 } 189 190 @Override 191 public String getShortDescription(Locale locale) { 192 return Bundle.getMessage(locale, "Light_Short"); 193 } 194 195 @Override 196 public String getLongDescription(Locale locale) { 197 String namedBean = _selectNamedBean.getDescription(locale); 198 String state; 199 200 switch (_stateAddressing) { 201 case Direct: 202 state = Bundle.getMessage(locale, "AddressByDirect", _lightState._text); 203 break; 204 205 case Reference: 206 state = Bundle.getMessage(locale, "AddressByReference", _stateReference); 207 break; 208 209 case LocalVariable: 210 state = Bundle.getMessage(locale, "AddressByLocalVariable", _stateLocalVariable); 211 break; 212 213 case Formula: 214 state = Bundle.getMessage(locale, "AddressByFormula", _stateFormula); 215 break; 216 217 default: 218 throw new IllegalArgumentException("invalid _stateAddressing state: " + _stateAddressing.name()); 219 } 220 221 return Bundle.getMessage(locale, "Light_Long", namedBean, _is_IsNot.toString(), state); 222 } 223 224 /** {@inheritDoc} */ 225 @Override 226 public void setup() { 227 // Do nothing 228 } 229 230 /** {@inheritDoc} */ 231 @Override 232 public void registerListenersForThisClass() { 233 if (!_listenersAreRegistered) { 234 _selectNamedBean.addPropertyChangeListener("KnownState", this); 235 _listenersAreRegistered = true; 236 } 237 } 238 239 /** {@inheritDoc} */ 240 @Override 241 public void unregisterListenersForThisClass() { 242 if (_listenersAreRegistered) { 243 _selectNamedBean.removePropertyChangeListener("KnownState", this); 244 _listenersAreRegistered = false; 245 } 246 } 247 248 /** {@inheritDoc} */ 249 @Override 250 public void propertyChange(PropertyChangeEvent evt) { 251 getConditionalNG().execute(); 252 } 253 254 /** {@inheritDoc} */ 255 @Override 256 public void disposeMe() { 257 } 258 259 260 public enum LightState { 261 Off(Light.OFF, Bundle.getMessage("StateOff")), 262 On(Light.ON, Bundle.getMessage("StateOn")), 263 Other(-1, Bundle.getMessage("SensorOtherStatus")); 264 265 private final int _id; 266 private final String _text; 267 268 private LightState(int id, String text) { 269 this._id = id; 270 this._text = text; 271 } 272 273 public static LightState get(int id) { 274 switch (id) { 275 case Light.OFF: 276 return Off; 277 278 case Light.ON: 279 return On; 280 281 default: 282 return Other; 283 } 284 } 285 286 public int getID() { 287 return _id; 288 } 289 290 @Override 291 public String toString() { 292 return _text; 293 } 294 295 } 296 297 298 /** {@inheritDoc} */ 299 @Override 300 public void getUsageDetail(int level, NamedBean bean, List<NamedBeanUsageReport> report, NamedBean cdl) { 301 log.debug("getUsageReport :: ExpressionLight: bean = {}, report = {}", cdl, report); 302 _selectNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Expression); 303 } 304 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ExpressionLight.class); 305 306}