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