001package jmri.jmrit.logixng.actions; 002 003import java.util.*; 004 005import javax.annotation.Nonnull; 006 007import jmri.*; 008import jmri.jmrit.logixng.*; 009import jmri.jmrit.logixng.util.LogixNG_SelectNamedBean; 010import jmri.jmrit.logixng.util.ReferenceUtil; 011import jmri.jmrit.logixng.util.parser.*; 012import jmri.jmrit.logixng.util.parser.ExpressionNode; 013import jmri.jmrit.logixng.util.parser.RecursiveDescentParser; 014import jmri.util.TypeConversionUtil; 015 016/** 017 * This action triggers a reporter. 018 * 019 * @author Daniel Bergqvist Copyright 2021 020 * @author Dave Sand Copyright 2021 021 */ 022public class ActionReporter extends AbstractDigitalAction { 023 024 private final LogixNG_SelectNamedBean<Reporter> _selectNamedBean = 025 new LogixNG_SelectNamedBean<>( 026 this, Reporter.class, InstanceManager.getDefault(ReporterManager.class)); 027 028 private final LogixNG_SelectNamedBean<Memory> _selectMemoryNamedBean = 029 new LogixNG_SelectNamedBean<>( 030 this, Memory.class, InstanceManager.getDefault(MemoryManager.class)); 031 032 private ReporterValue _reporterValue = ReporterValue.CopyCurrentReport; 033 034 private NamedBeanAddressing _dataAddressing = NamedBeanAddressing.Direct; 035 private String _dataReference = ""; 036 private String _dataLocalVariable = ""; 037 private String _dataFormula = ""; 038 private ExpressionNode _dataExpressionNode; 039 040// private NamedBeanHandle<Memory> _memoryHandle; 041 042 public ActionReporter(String sys, String user) 043 throws BadUserNameException, BadSystemNameException { 044 super(sys, user); 045 } 046 047 @Override 048 public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws ParserException { 049 DigitalActionManager manager = InstanceManager.getDefault(DigitalActionManager.class); 050 String sysName = systemNames.get(getSystemName()); 051 String userName = userNames.get(getSystemName()); 052 if (sysName == null) sysName = manager.getAutoSystemName(); 053 ActionReporter copy = new ActionReporter(sysName, userName); 054 copy.setComment(getComment()); 055 _selectNamedBean.copy(copy._selectNamedBean); 056 _selectMemoryNamedBean.copy(copy._selectMemoryNamedBean); 057 058 copy.setReporterValue(_reporterValue); 059 060 copy.setDataAddressing(_dataAddressing); 061 copy.setDataReference(_dataReference); 062 copy.setDataLocalVariable(_dataLocalVariable); 063 copy.setDataFormula(_dataFormula); 064// if (_memoryHandle != null) copy.setMemory(_memoryHandle); 065 066 return manager.registerAction(copy); 067 } 068 069 public LogixNG_SelectNamedBean<Reporter> getSelectNamedBean() { 070 return _selectNamedBean; 071 } 072 073 public LogixNG_SelectNamedBean<Memory> getSelectMemoryNamedBean() { 074 return _selectMemoryNamedBean; 075 } 076 077 public void setReporterValue(ReporterValue value) { 078 _reporterValue = value; 079 } 080 081 public ReporterValue getReporterValue() { 082 return _reporterValue; 083 } 084 085 public void setDataAddressing(NamedBeanAddressing addressing) throws ParserException { 086 _dataAddressing = addressing; 087 parseDataFormula(); 088 } 089 090 public NamedBeanAddressing getDataAddressing() { 091 return _dataAddressing; 092 } 093 094 public void setDataReference(@Nonnull String reference) { 095 if ((! reference.isEmpty()) && (! ReferenceUtil.isReference(reference))) { 096 throw new IllegalArgumentException("The reference \"" + reference + "\" is not a valid reference"); 097 } 098 _dataReference = reference; 099 } 100 101 public String getDataReference() { 102 return _dataReference; 103 } 104 105 public void setDataLocalVariable(@Nonnull String localVariable) { 106 _dataLocalVariable = localVariable; 107 } 108 109 public String getDataLocalVariable() { 110 return _dataLocalVariable; 111 } 112 113 public void setDataFormula(@Nonnull String formula) throws ParserException { 114 _dataFormula = formula; 115 parseDataFormula(); 116 } 117 118 public String getDataFormula() { 119 return _dataFormula; 120 } 121 122 private void parseDataFormula() throws ParserException { 123 if (_dataAddressing == NamedBeanAddressing.Formula) { 124 Map<String, Variable> variables = new HashMap<>(); 125 126 RecursiveDescentParser parser = new RecursiveDescentParser(variables); 127 _dataExpressionNode = parser.parseExpression(_dataFormula); 128 } else { 129 _dataExpressionNode = null; 130 } 131 } 132 133 134 /** {@inheritDoc} */ 135 @Override 136 public LogixNG_Category getCategory() { 137 return LogixNG_Category.ITEM; 138 } 139 140 Object getReporterData(Reporter reporter) throws JmriException { 141 Object obj; 142 ReporterValue value = _reporterValue; 143 144 switch (value) { 145 case CopyCurrentReport: 146 obj = reporter.getCurrentReport(); 147 break; 148 case CopyLastReport: 149 obj = reporter.getLastReport(); 150 break; 151 case CopyState: 152 obj = reporter.getState(); 153 break; 154 default: 155 throw new IllegalArgumentException("invalid value name: " + value.name()); 156 } 157 158 return obj; 159 } 160 161 void updateDestination(Object data) throws JmriException { 162 switch (_dataAddressing) { 163 case Direct: 164 Memory memory = _selectMemoryNamedBean.evaluateNamedBean(getConditionalNG()); 165 if (memory != null) { 166 memory.setValue(data); 167 } 168 break; 169 170 case Reference: 171 String refName = ReferenceUtil.getReference( 172 getConditionalNG().getSymbolTable(), _dataReference); 173 log.debug("ref ref = {}, name = {}", _dataReference, refName); 174 Memory refMem = InstanceManager.getDefault(MemoryManager.class).getMemory(refName); 175 if (refMem == null) { 176 throw new IllegalArgumentException("invalid memory reference: " + refName); 177 } 178 refMem.setValue(data); 179 break; 180 181 case LocalVariable: 182 log.debug("LocalVariable: lv = {}", _dataLocalVariable); 183 getConditionalNG().getSymbolTable().setValue(_dataLocalVariable, data); 184 break; 185 186 case Formula: 187 String formulaName = _dataExpressionNode != null 188 ? TypeConversionUtil.convertToString( 189 _dataExpressionNode.calculate( 190 getConditionalNG().getSymbolTable()), false) 191 : null; 192 if (formulaName == null) { 193 throw new IllegalArgumentException("invalid memory formula, name is null"); 194 } 195 196 Memory formulaMem = InstanceManager.getDefault(MemoryManager.class).getMemory(formulaName); 197 if (formulaMem == null) { 198 throw new IllegalArgumentException("invalid memory formula: " + formulaName); 199 } 200 formulaMem.setValue(data); 201 break; 202 203 default: 204 throw new IllegalArgumentException("invalid _dataAddressing state: " + _dataAddressing.name()); 205 } 206 } 207 208 /** {@inheritDoc} */ 209 @Override 210 public void execute() throws JmriException { 211 // Get the reporter bean 212 Reporter reporter = _selectNamedBean.evaluateNamedBean(getConditionalNG()); 213 if (reporter == null) return; 214 log.debug("reporter = {}", reporter.getDisplayName()); 215 216 // Get the reporter data 217 Object data = getReporterData(reporter); 218 log.debug("data = {}", data); 219 220 // Update the destination 221 updateDestination(data); 222 } 223 224 @Override 225 public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException { 226 throw new UnsupportedOperationException("Not supported."); 227 } 228 229 @Override 230 public int getChildCount() { 231 return 0; 232 } 233 234 @Override 235 public String getShortDescription(Locale locale) { 236 return Bundle.getMessage(locale, "ActionReporter_Short"); 237 } 238 239 @Override 240 public String getLongDescription(Locale locale) { 241 String bean = _selectNamedBean.getDescription(locale); 242 String dest = ""; 243 244 switch (_dataAddressing) { 245 case Direct: 246 String memoryName = _selectMemoryNamedBean.getDescription(locale); 247 dest = Bundle.getMessage(locale, "AddressByDirect", memoryName); 248 break; 249 250 case Reference: 251 dest = Bundle.getMessage(locale, "AddressByReference", _dataReference); 252 break; 253 254 case LocalVariable: 255 dest = Bundle.getMessage(locale, "AddressByLocalVariable", _dataLocalVariable); 256 break; 257 258 case Formula: 259 dest = Bundle.getMessage(locale, "AddressByFormula", _dataFormula); 260 break; 261 262 default: 263 throw new IllegalArgumentException("invalid _dataAddressing state: " + _dataAddressing.name()); 264 } 265 266 String item = getReporterValue().toString(); 267 268 return Bundle.getMessage(locale, "ActionReporter_Long", item, bean, dest); 269 } 270 271 /** {@inheritDoc} */ 272 @Override 273 public void setup() { 274 // Do nothing 275 } 276 277 /** {@inheritDoc} */ 278 @Override 279 public void disposeMe() { 280 } 281 282 public enum ReporterValue { 283 CopyCurrentReport(Bundle.getMessage("ActionReporter_CopyCurrentReport")), 284 CopyLastReport(Bundle.getMessage("ActionReporter_CopyLastReport")), 285 CopyState(Bundle.getMessage("ActionReporter_CopyState")); 286 287 private final String _text; 288 289 private ReporterValue(String text) { 290 this._text = text; 291 } 292 293 @Override 294 public String toString() { 295 return _text; 296 } 297 298 } 299 300 /** {@inheritDoc} */ 301 @Override 302 public void getUsageDetail(int level, NamedBean bean, List<NamedBeanUsageReport> report, NamedBean cdl) { 303 _selectNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Action); 304 _selectMemoryNamedBean.getUsageDetail(level, bean, report, cdl, this, LogixNG_SelectNamedBean.Type.Action); 305 } 306 307 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ActionReporter.class); 308 309}