001package jmri.jmrit.logixng.actions; 002 003import java.beans.PropertyChangeEvent; 004import java.beans.PropertyChangeListener; 005import java.util.HashMap; 006import java.util.List; 007import java.util.Locale; 008import java.util.Map; 009 010import javax.annotation.Nonnull; 011 012import jmri.*; 013import jmri.jmrit.logixng.*; 014import jmri.jmrit.logixng.util.LogixNG_SelectEnum; 015import jmri.jmrit.logixng.util.LogixNG_SelectNamedBean; 016import jmri.jmrit.logixng.util.ReferenceUtil; 017import jmri.jmrit.logixng.util.parser.ExpressionNode; 018import jmri.jmrit.logixng.util.parser.ParserException; 019import jmri.jmrit.logixng.util.parser.RecursiveDescentParser; 020import jmri.jmrit.logixng.util.parser.Variable; 021import jmri.util.ThreadingUtil; 022import jmri.util.TypeConversionUtil; 023 024/** 025 * This action sets the state of a light. 026 * 027 * @author Daniel Bergqvist Copyright 2018 028 */ 029public class ActionLight extends AbstractDigitalAction 030 implements PropertyChangeListener { 031 032 private final LogixNG_SelectNamedBean<Light> _selectNamedBean = 033 new LogixNG_SelectNamedBean<>( 034 this, Light.class, InstanceManager.getDefault(LightManager.class), this); 035 036 private final LogixNG_SelectEnum<LightState> _selectEnum = 037 new LogixNG_SelectEnum<>(this, LightState.values(), LightState.On, this); 038 039 private NamedBeanAddressing _dataAddressing = NamedBeanAddressing.Direct; 040 private String _dataReference = ""; 041 private String _dataLocalVariable = ""; 042 private String _dataFormula = ""; 043 private ExpressionNode _dataExpressionNode; 044 045 private int _lightValue = 0; 046 047 048 public ActionLight(String sys, String user) 049 throws BadUserNameException, BadSystemNameException { 050 super(sys, user); 051 } 052 053 @Override 054 public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws ParserException { 055 DigitalActionManager manager = InstanceManager.getDefault(DigitalActionManager.class); 056 String sysName = systemNames.get(getSystemName()); 057 String userName = userNames.get(getSystemName()); 058 if (sysName == null) sysName = manager.getAutoSystemName(); 059 ActionLight copy = new ActionLight(sysName, userName); 060 copy.setComment(getComment()); 061 _selectNamedBean.copy(copy._selectNamedBean); 062 _selectEnum.copy(copy._selectEnum); 063 064 copy.setDataAddressing(_dataAddressing); 065 copy.setDataReference(_dataReference); 066 copy.setDataLocalVariable(_dataLocalVariable); 067 copy.setDataFormula(_dataFormula); 068 069 copy.setLightValue(_lightValue); 070 071 return manager.registerAction(copy); 072 } 073 074 public LogixNG_SelectNamedBean<Light> getSelectNamedBean() { 075 return _selectNamedBean; 076 } 077 078 public LogixNG_SelectEnum<LightState> getSelectEnum() { 079 return _selectEnum; 080 } 081 082 public void setDataAddressing(NamedBeanAddressing addressing) throws ParserException { 083 _dataAddressing = addressing; 084 parseDataFormula(); 085 } 086 087 public NamedBeanAddressing getDataAddressing() { 088 return _dataAddressing; 089 } 090 091 public void setDataReference(@Nonnull String reference) { 092 if ((! reference.isEmpty()) && (! ReferenceUtil.isReference(reference))) { 093 throw new IllegalArgumentException("The reference \"" + reference + "\" is not a valid reference"); 094 } 095 _dataReference = reference; 096 } 097 098 public String getDataReference() { 099 return _dataReference; 100 } 101 102 public void setDataLocalVariable(@Nonnull String localVariable) { 103 _dataLocalVariable = localVariable; 104 } 105 106 public String getDataLocalVariable() { 107 return _dataLocalVariable; 108 } 109 110 public void setDataFormula(@Nonnull String formula) throws ParserException { 111 _dataFormula = formula; 112 parseDataFormula(); 113 } 114 115 public String getDataFormula() { 116 return _dataFormula; 117 } 118 119 private void parseDataFormula() throws ParserException { 120 if (_dataAddressing == NamedBeanAddressing.Formula) { 121 Map<String, Variable> variables = new HashMap<>(); 122 123 RecursiveDescentParser parser = new RecursiveDescentParser(variables); 124 _dataExpressionNode = parser.parseExpression(_dataFormula); 125 } else { 126 _dataExpressionNode = null; 127 } 128 } 129 130 131 public void setLightValue(int value) { 132 _lightValue = value; 133 } 134 135 public int getLightValue() { 136 return _lightValue; 137 } 138 139 /** {@inheritDoc} */ 140 @Override 141 public Category getCategory() { 142 return Category.ITEM; 143 } 144 145 private int getNewData(SymbolTable symbolTable) throws JmriException { 146 String newValue = ""; 147 148 switch (_dataAddressing) { 149 case Direct: 150 return _lightValue; 151 152 case Reference: 153 newValue = ReferenceUtil.getReference(symbolTable, _dataReference); 154 break; 155 156 case LocalVariable: 157 newValue = TypeConversionUtil 158 .convertToString(symbolTable.getValue(_dataLocalVariable), false); 159 break; 160 161 case Formula: 162 newValue = _dataExpressionNode != null 163 ? TypeConversionUtil.convertToString( 164 _dataExpressionNode.calculate(symbolTable), false) 165 : ""; 166 break; 167 168 default: 169 throw new IllegalArgumentException("invalid _addressing state: " + _dataAddressing.name()); 170 } 171 try { 172 int newInt = Integer.parseInt(newValue); 173 if (newInt < 0) newInt = 0; 174 if (newInt > 100) newInt = 100; 175 return newInt; 176 } catch (NumberFormatException ex) { 177 return 0; 178 } 179 } 180 181 /** {@inheritDoc} */ 182 @Override 183 public void execute() throws JmriException { 184 Light light = _selectNamedBean.evaluateNamedBean(getConditionalNG()); 185 186 if (light == null) return; 187 188 LightState state = _selectEnum.evaluateEnum(getConditionalNG()); 189 190 SymbolTable symbolTable = getConditionalNG().getSymbolTable(); 191 192 ThreadingUtil.runOnLayoutWithJmriException(() -> { 193 if (state == LightState.Toggle) { 194 if (light.getKnownState() == Turnout.CLOSED) { 195 light.setCommandedState(Turnout.THROWN); 196 } else { 197 light.setCommandedState(Turnout.CLOSED); 198 } 199 200 } else if (state == LightState.Intensity) { 201 if (light instanceof VariableLight) { 202 ((VariableLight)light).setTargetIntensity(getNewData(symbolTable) / 100.0); 203 } else { 204 light.setCommandedState(getNewData(symbolTable) > 50 ? Light.ON : Light.OFF); 205 } 206 } else if (state == LightState.Interval) { 207 if (light instanceof VariableLight) { 208 ((VariableLight)light).setTransitionTime(getNewData(symbolTable)); 209 } 210 } else { 211 light.setCommandedState(state.getID()); 212 } 213 }); 214 } 215 216 @Override 217 public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException { 218 throw new UnsupportedOperationException("Not supported."); 219 } 220 221 @Override 222 public int getChildCount() { 223 return 0; 224 } 225 226 @Override 227 public String getShortDescription(Locale locale) { 228 return Bundle.getMessage(locale, "Light_Short"); 229 } 230 231 @Override 232 public String getLongDescription(Locale locale) { 233 String namedBean = _selectNamedBean.getDescription(locale); 234 String state = _selectEnum.getDescription(locale); 235 236 if (_selectEnum.getAddressing() == NamedBeanAddressing.Direct) { 237 if (_selectEnum.getEnum() == LightState.Intensity || _selectEnum.getEnum() == LightState.Interval) { 238 String bundleKey = "Light_Long_Value"; 239 switch (_dataAddressing) { 240 case Direct: 241 String type = _selectEnum.getEnum() == LightState.Intensity ? 242 Bundle.getMessage("Light_Intensity_Value") : 243 Bundle.getMessage("Light_Interval_Value"); 244 return Bundle.getMessage(locale, bundleKey, namedBean, type, _lightValue); 245 case Reference: 246 return Bundle.getMessage(locale, bundleKey, namedBean, "", Bundle.getMessage("AddressByReference", _dataReference)); 247 case LocalVariable: 248 return Bundle.getMessage(locale, bundleKey, namedBean, "", Bundle.getMessage("AddressByLocalVariable", _dataLocalVariable)); 249 case Formula: 250 return Bundle.getMessage(locale, bundleKey, namedBean, "", Bundle.getMessage("AddressByFormula", _dataFormula)); 251 default: 252 throw new IllegalArgumentException("invalid _dataAddressing state: " + _dataAddressing.name()); 253 } 254 } 255 } 256 257 return Bundle.getMessage(locale, "Light_Long", namedBean, state); 258 } 259 260 /** {@inheritDoc} */ 261 @Override 262 public void setup() { 263 // Do nothing 264 } 265 266 /** {@inheritDoc} */ 267 @Override 268 public void registerListenersForThisClass() { 269 _selectNamedBean.registerListeners(); 270 _selectEnum.registerListeners(); 271 } 272 273 /** {@inheritDoc} */ 274 @Override 275 public void unregisterListenersForThisClass() { 276 _selectNamedBean.unregisterListeners(); 277 _selectEnum.unregisterListeners(); 278 } 279 280 /** {@inheritDoc} */ 281 @Override 282 public void disposeMe() { 283 } 284 285 286 // This constant is only used internally in LightState but must be outside 287 // the enum. 288 private static final int TOGGLE_ID = -1; 289 private static final int INTENSITY_ID = -2; 290 private static final int INTERVAL_ID = -3; 291 292 293 public enum LightState { 294 Off(Light.OFF, Bundle.getMessage("StateOff")), 295 On(Light.ON, Bundle.getMessage("StateOn")), 296 Toggle(TOGGLE_ID, Bundle.getMessage("LightToggleStatus")), 297 Intensity(INTENSITY_ID, Bundle.getMessage("LightIntensity")), 298 Interval(INTERVAL_ID, Bundle.getMessage("LightInterval")), 299 Unknown(Light.UNKNOWN, Bundle.getMessage("BeanStateUnknown")), 300 Inconsistent(Light.INCONSISTENT, Bundle.getMessage("BeanStateInconsistent")); 301 302 private final int _id; 303 private final String _text; 304 305 private LightState(int id, String text) { 306 this._id = id; 307 this._text = text; 308 } 309 310 static public LightState get(int id) { 311 switch (id) { 312 case Light.UNKNOWN: 313 return Unknown; 314 315 case Light.INCONSISTENT: 316 return Inconsistent; 317 318 case Light.OFF: 319 return Off; 320 321 case Light.ON: 322 return On; 323 324 case TOGGLE_ID: 325 return Toggle; 326 327 default: 328 throw new IllegalArgumentException("invalid light state"); 329 } 330 } 331 332 public int getID() { 333 return _id; 334 } 335 336 @Override 337 public String toString() { 338 return _text; 339 } 340 341 } 342 343 /** {@inheritDoc} */ 344 @Override 345 public void getUsageDetail(int level, NamedBean bean, List<NamedBeanUsageReport> report, NamedBean cdl) { 346 _selectNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Action); 347 } 348 349 /** {@inheritDoc} */ 350 @Override 351 public void propertyChange(PropertyChangeEvent evt) { 352 getConditionalNG().execute(); 353 } 354 355// private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ActionLight.class); 356 357}