001package jmri.jmrit.logixng.actions; 002 003import java.beans.*; 004import java.util.*; 005import java.util.concurrent.atomic.AtomicReference; 006 007import javax.annotation.Nonnull; 008 009import jmri.*; 010import jmri.jmrit.logixng.*; 011import jmri.jmrit.logixng.util.LogixNG_SelectNamedBean; 012import jmri.jmrit.logixng.util.ReferenceUtil; 013import jmri.jmrit.logixng.util.parser.*; 014import jmri.util.TypeConversionUtil; 015 016/** 017 * Evaluates the state of a SignalMast. 018 * 019 * @author Daniel Bergqvist Copyright 2020 020 */ 021public class ActionSignalMast extends AbstractDigitalAction 022 implements PropertyChangeListener, VetoableChangeListener { 023 024 private final LogixNG_SelectNamedBean<SignalMast> _selectNamedBean = 025 new LogixNG_SelectNamedBean<>( 026 this, SignalMast.class, InstanceManager.getDefault(SignalMastManager.class), this); 027 028 private NamedBeanAddressing _operationAddressing = NamedBeanAddressing.Direct; 029 private OperationType _operationType = OperationType.Aspect; 030 private String _operationReference = ""; 031 private String _operationLocalVariable = ""; 032 private String _operationFormula = ""; 033 private ExpressionNode _operationExpressionNode; 034 035 private NamedBeanAddressing _aspectAddressing = NamedBeanAddressing.Direct; 036 private String _signalMastAspect = ""; 037 private String _aspectReference = ""; 038 private String _aspectLocalVariable = ""; 039 private String _aspectFormula = ""; 040 private ExpressionNode _aspectExpressionNode; 041 042 private final LogixNG_SelectNamedBean<SignalMast> _selectExampleNamedBean = 043 new LogixNG_SelectNamedBean<>( 044 this, SignalMast.class, InstanceManager.getDefault(SignalMastManager.class), this); 045 046 047 public ActionSignalMast(String sys, String user) 048 throws BadUserNameException, BadSystemNameException { 049 super(sys, user); 050 } 051 052 @Override 053 public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws JmriException { 054 DigitalActionManager manager = InstanceManager.getDefault(DigitalActionManager.class); 055 String sysName = systemNames.get(getSystemName()); 056 String userName = userNames.get(getSystemName()); 057 if (sysName == null) sysName = manager.getAutoSystemName(); 058 ActionSignalMast copy = new ActionSignalMast(sysName, userName); 059 copy.setComment(getComment()); 060 _selectNamedBean.copy(copy._selectNamedBean); 061 copy.setAspect(_signalMastAspect); 062 copy.setOperationAddressing(_operationAddressing); 063 copy.setOperationType(_operationType); 064 copy.setOperationFormula(_operationFormula); 065 copy.setOperationLocalVariable(_operationLocalVariable); 066 copy.setOperationReference(_operationReference); 067 copy.setAspectAddressing(_aspectAddressing); 068 copy.setAspectFormula(_aspectFormula); 069 copy.setAspectLocalVariable(_aspectLocalVariable); 070 copy.setAspectReference(_aspectReference); 071 _selectExampleNamedBean.copy(copy._selectExampleNamedBean); 072 return manager.registerAction(copy).deepCopyChildren(this, systemNames, userNames); 073 } 074 075 public LogixNG_SelectNamedBean<SignalMast> getSelectNamedBean() { 076 return _selectNamedBean; 077 } 078 079 public LogixNG_SelectNamedBean<SignalMast> getSelectExampleNamedBean() { 080 return _selectExampleNamedBean; 081 } 082 083 public void setOperationAddressing(NamedBeanAddressing addressing) throws ParserException { 084 _operationAddressing = addressing; 085 parseOperationFormula(); 086 } 087 088 public NamedBeanAddressing getOperationAddressing() { 089 return _operationAddressing; 090 } 091 092 public void setOperationType(OperationType operationType) { 093 _operationType = operationType; 094 } 095 096 public OperationType getOperationType() { 097 return _operationType; 098 } 099 100 public void setOperationReference(@Nonnull String reference) { 101 if ((! reference.isEmpty()) && (! ReferenceUtil.isReference(reference))) { 102 throw new IllegalArgumentException("The reference \"" + reference + "\" is not a valid reference"); 103 } 104 _operationReference = reference; 105 } 106 107 public String getOperationReference() { 108 return _operationReference; 109 } 110 111 public void setOperationLocalVariable(@Nonnull String localVariable) { 112 _operationLocalVariable = localVariable; 113 } 114 115 public String getOperationLocalVariable() { 116 return _operationLocalVariable; 117 } 118 119 public void setOperationFormula(@Nonnull String formula) throws ParserException { 120 _operationFormula = formula; 121 parseOperationFormula(); 122 } 123 124 public String getOperationFormula() { 125 return _operationFormula; 126 } 127 128 private void parseOperationFormula() throws ParserException { 129 if (_operationAddressing == NamedBeanAddressing.Formula) { 130 Map<String, Variable> variables = new HashMap<>(); 131 132 RecursiveDescentParser parser = new RecursiveDescentParser(variables); 133 _operationExpressionNode = parser.parseExpression(_operationFormula); 134 } else { 135 _operationExpressionNode = null; 136 } 137 } 138 139 public void setAspectAddressing(NamedBeanAddressing addressing) throws ParserException { 140 _aspectAddressing = addressing; 141 parseAspectFormula(); 142 } 143 144 public NamedBeanAddressing getAspectAddressing() { 145 return _aspectAddressing; 146 } 147 148 public void setAspect(String aspect) { 149 if (aspect == null) _signalMastAspect = ""; 150 else _signalMastAspect = aspect; 151 } 152 153 public String getAspect() { 154 return _signalMastAspect; 155 } 156 157 public void setAspectReference(@Nonnull String reference) { 158 if ((! reference.isEmpty()) && (! ReferenceUtil.isReference(reference))) { 159 throw new IllegalArgumentException("The reference \"" + reference + "\" is not a valid reference"); 160 } 161 _aspectReference = reference; 162 } 163 164 public String getAspectReference() { 165 return _aspectReference; 166 } 167 168 public void setAspectLocalVariable(@Nonnull String localVariable) { 169 _aspectLocalVariable = localVariable; 170 } 171 172 public String getAspectLocalVariable() { 173 return _aspectLocalVariable; 174 } 175 176 public void setAspectFormula(@Nonnull String formula) throws ParserException { 177 _aspectFormula = formula; 178 parseAspectFormula(); 179 } 180 181 public String getAspectFormula() { 182 return _aspectFormula; 183 } 184 185 private void parseAspectFormula() throws ParserException { 186 if (_aspectAddressing == NamedBeanAddressing.Formula) { 187 Map<String, Variable> variables = new HashMap<>(); 188 189 RecursiveDescentParser parser = new RecursiveDescentParser(variables); 190 _aspectExpressionNode = parser.parseExpression(_aspectFormula); 191 } else { 192 _aspectExpressionNode = null; 193 } 194 } 195 196 /** {@inheritDoc} */ 197 @Override 198 public Category getCategory() { 199 return Category.ITEM; 200 } 201 202 private String getNewAspect(ConditionalNG conditionalNG) throws JmriException { 203 204 switch (_aspectAddressing) { 205 case Direct: 206 return _signalMastAspect; 207 208 case Reference: 209 return ReferenceUtil.getReference( 210 conditionalNG.getSymbolTable(), _aspectReference); 211 212 case LocalVariable: 213 SymbolTable symbolTable = conditionalNG.getSymbolTable(); 214 return TypeConversionUtil 215 .convertToString(symbolTable.getValue(_aspectLocalVariable), false); 216 217 case Formula: 218 return _aspectExpressionNode != null 219 ? TypeConversionUtil.convertToString( 220 _aspectExpressionNode.calculate( 221 conditionalNG.getSymbolTable()), false) 222 : ""; 223 224 default: 225 throw new IllegalArgumentException("invalid _aspectAddressing state: " + _aspectAddressing.name()); 226 } 227 } 228 229 private OperationType getOperation(ConditionalNG conditionalNG) throws JmriException { 230 231 String oper = ""; 232 try { 233 switch (_operationAddressing) { 234 case Direct: 235 return _operationType; 236 237 case Reference: 238 oper = ReferenceUtil.getReference( 239 conditionalNG.getSymbolTable(), _operationReference); 240 return OperationType.valueOf(oper); 241 242 case LocalVariable: 243 SymbolTable symbolTable = conditionalNG.getSymbolTable(); 244 oper = TypeConversionUtil 245 .convertToString(symbolTable.getValue(_operationLocalVariable), false); 246 return OperationType.valueOf(oper); 247 248 case Formula: 249 if (_aspectExpressionNode != null) { 250 oper = TypeConversionUtil.convertToString( 251 _operationExpressionNode.calculate( 252 conditionalNG.getSymbolTable()), false); 253 return OperationType.valueOf(oper); 254 } else { 255 return null; 256 } 257 default: 258 throw new IllegalArgumentException("invalid _addressing state: " + _operationAddressing.name()); 259 } 260 } catch (IllegalArgumentException e) { 261 throw new JmriException("Unknown operation: "+oper, e); 262 } 263 } 264 265 /** {@inheritDoc} */ 266 @Override 267 public void execute() throws JmriException { 268 final ConditionalNG conditionalNG = getConditionalNG(); 269 270 SignalMast signalMast = _selectNamedBean.evaluateNamedBean(conditionalNG); 271 272 if (signalMast == null) return; 273 274 OperationType operation = getOperation(conditionalNG); 275 276 AtomicReference<JmriException> ref = new AtomicReference<>(); 277 jmri.util.ThreadingUtil.runOnLayoutWithJmriException(() -> { 278 try { 279 switch (operation) { 280 case Aspect: 281 String newAspect = getNewAspect(conditionalNG); 282 if (!newAspect.isEmpty()) { 283 signalMast.setAspect(newAspect); 284 } 285 break; 286 case Lit: 287 signalMast.setLit(true); 288 break; 289 case NotLit: 290 signalMast.setLit(false); 291 break; 292 case Held: 293 signalMast.setHeld(true); 294 break; 295 case NotHeld: 296 signalMast.setHeld(false); 297 break; 298 case PermissiveSmlDisabled: 299 signalMast.setPermissiveSmlDisabled(true); 300 break; 301 case PermissiveSmlNotDisabled: 302 signalMast.setPermissiveSmlDisabled(false); 303 break; 304 default: 305 throw new JmriException("Unknown enum: "+_operationType.name()); 306 } 307 } catch (JmriException e) { 308 ref.set(e); 309 } 310 }); 311 if (ref.get() != null) throw ref.get(); 312 } 313 314 @Override 315 public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException { 316 throw new UnsupportedOperationException("Not supported."); 317 } 318 319 @Override 320 public int getChildCount() { 321 return 0; 322 } 323 324 @Override 325 public String getShortDescription(Locale locale) { 326 return Bundle.getMessage(locale, "SignalMast_Short"); 327 } 328 329 @Override 330 public String getLongDescription(Locale locale) { 331 String namedBean = _selectNamedBean.getDescription(locale); 332 String operation; 333 String aspect; 334 335 switch (_operationAddressing) { 336 case Direct: 337 operation = Bundle.getMessage(locale, "AddressByDirect", _operationType._text); 338 break; 339 340 case Reference: 341 operation = Bundle.getMessage(locale, "AddressByReference", _operationReference); 342 break; 343 344 case LocalVariable: 345 operation = Bundle.getMessage(locale, "AddressByLocalVariable", _operationLocalVariable); 346 break; 347 348 case Formula: 349 operation = Bundle.getMessage(locale, "AddressByFormula", _operationFormula); 350 break; 351 352 default: 353 throw new IllegalArgumentException("invalid _operationAddressing state: " + _operationAddressing.name()); 354 } 355 356 switch (_aspectAddressing) { 357 case Direct: 358 aspect = Bundle.getMessage(locale, "AddressByDirect", _signalMastAspect); 359 break; 360 361 case Reference: 362 aspect = Bundle.getMessage(locale, "AddressByReference", _aspectReference); 363 break; 364 365 case LocalVariable: 366 aspect = Bundle.getMessage(locale, "AddressByLocalVariable", _aspectLocalVariable); 367 break; 368 369 case Formula: 370 aspect = Bundle.getMessage(locale, "AddressByFormula", _aspectFormula); 371 break; 372 373 default: 374 throw new IllegalArgumentException("invalid _stateAddressing state: " + _aspectAddressing.name()); 375 } 376 377 if (_operationAddressing == NamedBeanAddressing.Direct) { 378 if (_operationType == OperationType.Aspect) { 379 return Bundle.getMessage(locale, "SignalMast_LongAspect", namedBean, aspect); 380 } else { 381 return Bundle.getMessage(locale, "SignalMast_Long", namedBean, operation); 382 } 383 } else { 384 return Bundle.getMessage(locale, "SignalMast_LongUnknownOper", namedBean, operation, aspect); 385 } 386 } 387 388 /** {@inheritDoc} */ 389 @Override 390 public void setup() { 391 // Do nothing 392 } 393 394 /** {@inheritDoc} */ 395 @Override 396 public void registerListenersForThisClass() { 397 _selectNamedBean.registerListeners(); 398 } 399 400 /** {@inheritDoc} */ 401 @Override 402 public void unregisterListenersForThisClass() { 403 _selectNamedBean.unregisterListeners(); 404 } 405 406 /** {@inheritDoc} */ 407 @Override 408 public void propertyChange(PropertyChangeEvent evt) { 409 getConditionalNG().execute(); 410 } 411 412 /** {@inheritDoc} */ 413 @Override 414 public void disposeMe() { 415 } 416 417 418 419 public enum OperationType { 420 Aspect(Bundle.getMessage("SignalMastOperationType_Aspect")), 421 Lit(Bundle.getMessage("SignalMastOperationType_Lit")), 422 NotLit(Bundle.getMessage("SignalMastOperationType_NotLit")), 423 Held(Bundle.getMessage("SignalMastOperationType_Held")), 424 NotHeld(Bundle.getMessage("SignalMastOperationType_NotHeld")), 425 PermissiveSmlDisabled(Bundle.getMessage("SignalMastOperationType_PermissiveSmlDisabled")), 426 PermissiveSmlNotDisabled(Bundle.getMessage("SignalMastOperationType_PermissiveSmlNotDisabled")); 427 428 private final String _text; 429 430 private OperationType(String text) { 431 this._text = text; 432 } 433 434 @Override 435 public String toString() { 436 return _text; 437 } 438 439 } 440 441 /** {@inheritDoc} */ 442 @Override 443 public void getUsageDetail(int level, NamedBean bean, List<NamedBeanUsageReport> report, NamedBean cdl) { 444 log.debug("getUsageReport :: ActionSignalMast: bean = {}, report = {}", cdl, report); 445 _selectNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Action); 446 _selectExampleNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Action); 447 } 448 449 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ActionSignalMast.class); 450 451}