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