001package jmri.jmrit.logixng.implementation; 002 003import java.util.*; 004 005import javax.annotation.Nonnull; 006 007import jmri.InstanceManager; 008import jmri.JmriException; 009import jmri.Manager; 010import jmri.jmrit.logixng.*; 011import jmri.jmrit.logixng.Module; 012import jmri.jmrit.logixng.Stack; 013import jmri.jmrit.logixng.util.LogixNG_Thread; 014import jmri.util.*; 015 016/** 017 * The default implementation of ConditionalNG. 018 * 019 * @author Daniel Bergqvist Copyright 2019 020 */ 021public class DefaultConditionalNG extends AbstractBase 022 implements ConditionalNG, FemaleSocketListener { 023 024 private final LogixNG_Thread _thread; 025 private int _startupThreadId; 026 private Base _parent = null; 027 private String _socketSystemName = null; 028 private final FemaleDigitalActionSocket _femaleSocket; 029 private boolean _enabled = true; 030 private boolean _executeAtStartup = true; 031 private final ExecuteLock _executeLock = new ExecuteLock(); 032 private boolean _runDelayed = true; 033 private final Stack _stack = new DefaultStack(); 034 private SymbolTable _symbolTable; 035 036 037 public DefaultConditionalNG(String sys, String user) 038 throws BadUserNameException, BadSystemNameException { 039 this(sys, user, LogixNG_Thread.DEFAULT_LOGIXNG_THREAD); 040 } 041 042 public DefaultConditionalNG(String sys, String user, int threadID) 043 throws BadUserNameException, BadSystemNameException { 044 super(sys, user); 045 046 _startupThreadId = threadID; 047 _thread = LogixNG_Thread.getThread(threadID); 048 _thread.setThreadInUse(); 049 050 // Do this test here to ensure all the tests are using correct system names 051 Manager.NameValidity isNameValid = InstanceManager.getDefault(ConditionalNG_Manager.class).validSystemNameFormat(mSystemName); 052 if (isNameValid != Manager.NameValidity.VALID) { 053 throw new IllegalArgumentException("system name is not valid"); 054 } 055 _femaleSocket = new DefaultFemaleDigitalActionSocket(this, this, "A"); 056 } 057 058 /** {@inheritDoc} */ 059 @Override 060 public LogixNG_Thread getCurrentThread() { 061 return _thread; 062 } 063 064 /** {@inheritDoc} */ 065 @Override 066 public int getStartupThreadId() { 067 return _startupThreadId; 068 } 069 070 /** {@inheritDoc} */ 071 @Override 072 public void setStartupThreadId(int threadId) { 073 int oldStartupThreadId = _startupThreadId; 074 _startupThreadId = threadId; 075 firePropertyChange("Thread", oldStartupThreadId, _startupThreadId); 076 } 077 078 /** {@inheritDoc} */ 079 @Override 080 public Base getParent() { 081 return _parent; 082 } 083 084 /** {@inheritDoc} */ 085 @Override 086 public void setParent(Base parent) { 087 _parent = parent; 088 089 if (isActive()) registerListeners(); 090 else unregisterListeners(); 091 } 092 093 /** {@inheritDoc} */ 094 @Override 095 public FemaleDigitalActionSocket getFemaleSocket() { 096 return _femaleSocket; 097 } 098 099 /** {@inheritDoc} */ 100 @Override 101 public void setRunDelayed(boolean value) { 102 _runDelayed = value; 103 } 104 105 /** {@inheritDoc} */ 106 @Override 107 public boolean getRunDelayed() { 108 return _runDelayed; 109 } 110 111 private void runOnLogixNG_Thread( 112 @Nonnull ThreadingUtil.ThreadAction ta, 113 boolean allowRunDelayed) { 114 115 if (_runDelayed && allowRunDelayed) { 116 _thread.runOnLogixNGEventually(ta); 117 } else { 118 _thread.runOnLogixNG(ta); 119 } 120 } 121 122 /** {@inheritDoc} */ 123 @Override 124 public void execute() { 125 if (_executeAtStartup || !getLogixNG().isStartup()) { 126 if (_executeLock.once()) { 127 runOnLogixNG_Thread(new ExecuteTask(this, _executeLock, null), true); 128 } 129 } 130 } 131 132 /** {@inheritDoc} */ 133 @Override 134 public void execute(boolean allowRunDelayed) { 135 if (_executeLock.once()) { 136 runOnLogixNG_Thread(new ExecuteTask(this, _executeLock, null), allowRunDelayed); 137 } 138 } 139 140 /** {@inheritDoc} */ 141 @Override 142 public void execute(FemaleDigitalActionSocket socket) { 143 runOnLogixNG_Thread(() -> {internalExecute(this, socket);}, true); 144 } 145 146 private static void internalExecute(ConditionalNG conditionalNG, FemaleDigitalActionSocket femaleSocket) { 147 if (conditionalNG.isEnabled()) { 148 DefaultSymbolTable newSymbolTable = new DefaultSymbolTable(conditionalNG); 149 150 try { 151 conditionalNG.setCurrentConditionalNG(conditionalNG); 152 153 conditionalNG.setSymbolTable(newSymbolTable); 154 155 InlineLogixNG inlineLogixNG = conditionalNG.getLogixNG().getInlineLogixNG(); 156 if (inlineLogixNG != null) { 157 List<SymbolTable.VariableData> localVariables = new ArrayList<>(); 158 localVariables.add(new SymbolTable.VariableData( 159 "__InlineLogixNG__", SymbolTable.InitialValueType.String, 160 inlineLogixNG.getNameString())); 161// localVariables.add(new SymbolTable.VariableData( 162// "__PositionableId__", SymbolTable.InitialValueType.String, 163// inlineLogixNG.getId())); 164 localVariables.add(new SymbolTable.VariableData( 165 "__Editor__", SymbolTable.InitialValueType.String, 166 inlineLogixNG.getEditorName())); 167 newSymbolTable.createSymbols(localVariables); 168 } 169 170 if (femaleSocket != null) { 171 femaleSocket.execute(); 172 } else { 173 conditionalNG.getFemaleSocket().execute(); 174 } 175 } catch (ReturnException | ExitException e) { 176 // A Return action in a ConditionalNG causes a ReturnException so this is okay. 177 // An Exit action in a ConditionalNG causes a ExitException so this is okay. 178 } catch (PassThruException e) { 179 // This happens due to a a Break action or a Continue action that isn't handled. 180 log.info("ConditionalNG {} was aborted during execute: {}", 181 conditionalNG.getSystemName(), e.getCause(), e.getCause()); 182 } catch (AbortConditionalNGExecutionException e) { 183 if (InstanceManager.getDefault(LogixNGPreferences.class).getShowSystemNameInException()) { 184 log.warn("ConditionalNG {} was aborted during execute in the item {}: {}", 185 conditionalNG.getSystemName(), e.getMaleSocket().getSystemName(), e.getCause(), e.getCause()); 186 } else { 187 log.warn("ConditionalNG {} was aborted during execute: {}", 188 conditionalNG.getSystemName(), e.getCause(), e.getCause()); 189 } 190 } catch (JmriException | RuntimeException e) { 191// LoggingUtil.warnOnce(log, "ConditionalNG {} got an exception during execute: {}", 192// conditionalNG.getSystemName(), e, e); 193 log.warn("ConditionalNG {} got an exception during execute: {}", 194 conditionalNG.getSystemName(), e, e); 195 } 196 197 conditionalNG.setSymbolTable(newSymbolTable.getPrevSymbolTable()); 198 } 199 } 200 201 private static class ExecuteTask implements ThreadingUtil.ThreadAction { 202 203 private final ConditionalNG _conditionalNG; 204 private final ExecuteLock _executeLock; 205 private final FemaleDigitalActionSocket _localFemaleSocket; 206 207 public ExecuteTask(ConditionalNG conditionalNG, ExecuteLock executeLock, FemaleDigitalActionSocket femaleSocket) { 208 _conditionalNG = conditionalNG; 209 _executeLock = executeLock; 210 _localFemaleSocket = femaleSocket; 211 } 212 213 @Override 214 public void run() { 215 while (_executeLock.loop()) { 216 internalExecute(_conditionalNG, _localFemaleSocket); 217 } 218 } 219 220 } 221 222 /** 223 * Set the current ConditionalNG. 224 * @param conditionalNG the current ConditionalNG 225 */ 226 @Override 227 public void setCurrentConditionalNG(ConditionalNG conditionalNG) { 228 if (this != conditionalNG) { 229 throw new UnsupportedOperationException("The new conditionalNG must be the same as myself"); 230 } 231 for (Module m : InstanceManager.getDefault(ModuleManager.class).getNamedBeanSet()) { 232 m.setCurrentConditionalNG(conditionalNG); 233 } 234 } 235 236 /** {@inheritDoc} */ 237 @Override 238 public Stack getStack() { 239 return _stack; 240 } 241 242 /** {@inheritDoc} */ 243 @Override 244 public SymbolTable getSymbolTable() { 245 return _symbolTable; 246 } 247 248 /** {@inheritDoc} */ 249 @Override 250 public void setSymbolTable(SymbolTable symbolTable) { 251 _symbolTable = symbolTable; 252 } 253 254 @Override 255 public String getBeanType() { 256 return Bundle.getMessage("BeanNameConditionalNG"); 257 } 258 259 @Override 260 public void setState(int s) throws JmriException { 261 log.warn("Unexpected call to setState in DefaultConditionalNG."); // NOI18N 262 } 263 264 @Override 265 public int getState() { 266 log.warn("Unexpected call to getState in DefaultConditionalNG."); // NOI18N 267 return UNKNOWN; 268 } 269 270 @Override 271 public void connected(FemaleSocket socket) { 272 _socketSystemName = socket.getConnectedSocket().getSystemName(); 273 } 274 275 @Override 276 public void disconnected(FemaleSocket socket) { 277 _socketSystemName = null; 278 } 279 280 @Override 281 public String getShortDescription(Locale locale) { 282 return "ConditionalNG: "+getDisplayName(); 283 } 284 285 @Override 286 public String getLongDescription(Locale locale) { 287 if (_thread.getThreadId() != LogixNG_Thread.DEFAULT_LOGIXNG_THREAD) { 288 return "ConditionalNG: "+getDisplayName() + " on thread " + _thread.getThreadName(); 289 } 290 return "ConditionalNG: "+getDisplayName(); 291 } 292 293 @Override 294 public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException { 295 if (index != 0) { 296 throw new IllegalArgumentException( 297 String.format("index has invalid value: %d", index)); 298 } 299 300 return _femaleSocket; 301 } 302 303 @Override 304 public int getChildCount() { 305 return 1; 306 } 307 308 @Override 309 public Category getCategory() { 310 throw new UnsupportedOperationException("Not supported."); 311 } 312 313 @Override 314 public void setSocketSystemName(String systemName) { 315 if ((systemName == null) || (!systemName.equals(_socketSystemName))) { 316 _femaleSocket.disconnect(); 317 } 318 _socketSystemName = systemName; 319 } 320 321 @Override 322 public String getSocketSystemName() { 323 return _socketSystemName; 324 } 325 326 /** {@inheritDoc} */ 327 @Override 328 final public void setup() { 329 if (!_femaleSocket.isConnected() 330 || !_femaleSocket.getConnectedSocket().getSystemName() 331 .equals(_socketSystemName)) { 332 333 _femaleSocket.disconnect(); 334 335 if (_socketSystemName != null) { 336 try { 337 MaleSocket maleSocket = 338 InstanceManager.getDefault(DigitalActionManager.class) 339 .getBySystemName(_socketSystemName); 340 if (maleSocket != null) { 341 _femaleSocket.connect(maleSocket); 342 maleSocket.setup(); 343 } else { 344 log.error("digital action is not found: {}", _socketSystemName); 345 } 346 } catch (SocketAlreadyConnectedException ex) { 347 // This shouldn't happen and is a runtime error if it does. 348 throw new RuntimeException("socket is already connected"); 349 } 350 } 351 } else { 352 _femaleSocket.setup(); 353 } 354 } 355 356 /** {@inheritDoc} */ 357 @Override 358 final public void disposeMe() { 359 _femaleSocket.dispose(); 360 } 361 362 /** {@inheritDoc} */ 363 @Override 364 public void setEnabled(boolean enable) { 365 _enabled = enable; 366 if (isActive()) { 367 LogixNG logixNG = getLogixNG(); 368 if ((logixNG != null) && logixNG.isActive()) { 369 registerListeners(); 370 if (_executeAtStartup) { 371 execute(); 372 } 373 } 374 } else { 375 unregisterListeners(); 376 } 377 } 378 379 /** {@inheritDoc} */ 380 @Override 381 public boolean isEnabled() { 382 return _enabled; 383 } 384 385 /** {@inheritDoc} */ 386 @Override 387 public void setExecuteAtStartup(boolean value) { 388 _executeAtStartup = value; 389 } 390 391 /** {@inheritDoc} */ 392 @Override 393 public boolean isExecuteAtStartup() { 394 return _executeAtStartup; 395 } 396 397 /** {@inheritDoc} */ 398 @Override 399 public synchronized boolean isListenersRegistered() { 400 return _listenersAreRegistered; 401 } 402 403 /** {@inheritDoc} */ 404 @Override 405 public synchronized void registerListenersForThisClass() { 406 _listenersAreRegistered = true; 407 } 408 409 /** {@inheritDoc} */ 410 @Override 411 public synchronized void unregisterListenersForThisClass() { 412 _listenersAreRegistered = false; 413 } 414 415 @Override 416 public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) { 417 throw new UnsupportedOperationException("Not supported yet."); 418 } 419 420 @Override 421 public boolean existsInTree() { 422 return true; 423 } 424 425 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultConditionalNG.class); 426 427}