001package jmri.jmrit.logixng.actions; 002 003import java.util.*; 004 005import jmri.*; 006import jmri.implementation.AbstractShutDownTask; 007import jmri.jmrit.logixng.*; 008import jmri.jmrit.logixng.implementation.DefaultFemaleDigitalActionSocket; 009import jmri.jmrit.logixng.implementation.DefaultSymbolTable; 010 011/** 012 * Executes a digital action delayed. 013 * 014 * @author Daniel Bergqvist Copyright 2022 015 */ 016public class ActionShutDownTask 017 extends AbstractDigitalAction 018 implements FemaleSocketListener { 019 020 private String _callSocketSystemName; 021 private String _runSocketSystemName; 022 private final FemaleDigitalExpressionSocket _callSocket; 023 private final FemaleDigitalActionSocket _runSocket; 024 private final Object _lock = new Object(); 025 026 027 public ActionShutDownTask(String sys, String user) { 028 super(sys, user); 029 _callSocket = InstanceManager.getDefault(DigitalExpressionManager.class) 030 .createFemaleSocket(this, this, "E"); 031 _runSocket = InstanceManager.getDefault(DigitalActionManager.class) 032 .createFemaleSocket(this, this, "A"); 033 } 034 035 @Override 036 public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws JmriException { 037 DigitalActionManager manager = InstanceManager.getDefault(DigitalActionManager.class); 038 String sysName = systemNames.get(getSystemName()); 039 String userName = userNames.get(getSystemName()); 040 if (sysName == null) sysName = manager.getAutoSystemName(); 041 ActionShutDownTask copy = new ActionShutDownTask(sysName, userName); 042 copy.setComment(getComment()); 043 return manager.registerAction(copy).deepCopyChildren(this, systemNames, userNames); 044 } 045 046 /** {@inheritDoc} */ 047 @Override 048 public Category getCategory() { 049 return Category.OTHER; 050 } 051 052 /** {@inheritDoc} */ 053 @Override 054 public void execute() throws JmriException { 055 ConditionalNG conditionalNG = getConditionalNG(); 056 DefaultSymbolTable newSymbolTable = new DefaultSymbolTable(conditionalNG.getSymbolTable()); 057 058 ShutDownTask shutDownTask = new AbstractShutDownTask("LogixNG action ShutDownTask") { 059 @Override 060 public Boolean call() { 061 if (_callSocket.isConnected()) { 062 InternalCallSocket internalSocket 063 = new InternalCallSocket(); 064 internalSocket.conditionalNG = conditionalNG; 065 internalSocket.newSymbolTable = newSymbolTable; 066 067 try { 068 synchronized(_lock) { 069 conditionalNG.execute(internalSocket); 070 while (!internalSocket._completed) _lock.wait(); 071 return internalSocket._result; 072 } 073 } catch (InterruptedException e) { 074 log.error("Interrupted exception: {}", e, e); 075 return true; 076 } 077 } else { 078 return true; 079 } 080 } 081 082 @Override 083 public void runEarly() { 084 if (_runSocket.isConnected()) { 085 InternalRunSocket internalSocket 086 = new InternalRunSocket(); 087 internalSocket.conditionalNG = conditionalNG; 088 internalSocket.newSymbolTable = newSymbolTable; 089 090 try { 091 synchronized(_lock) { 092 conditionalNG.execute(internalSocket); 093 while (!internalSocket._completed) _lock.wait(); 094 } 095 } catch (InterruptedException e) { 096 log.error("Interrupted exception: {}", e, e); 097 } 098 } 099 } 100 101 @Override 102 public void run() { 103 // Do nothing 104 } 105 }; 106 InstanceManager.getDefault(ShutDownManager.class).register(shutDownTask); 107 } 108 109 @Override 110 public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException { 111 switch (index) { 112 case 0: 113 return _callSocket; 114 115 case 1: 116 return _runSocket; 117 118 default: 119 throw new IllegalArgumentException( 120 String.format("index has invalid value: %d", index)); 121 } 122 } 123 124 @Override 125 public int getChildCount() { 126 return 2; 127 } 128 129 @Override 130 public void connected(FemaleSocket socket) { 131 if (socket == _callSocket) { 132 _callSocketSystemName = socket.getConnectedSocket().getSystemName(); 133 } else if (socket == _runSocket) { 134 _runSocketSystemName = socket.getConnectedSocket().getSystemName(); 135 } else { 136 throw new IllegalArgumentException("unkown socket"); 137 } 138 } 139 140 @Override 141 public void disconnected(FemaleSocket socket) { 142 if (socket == _callSocket) { 143 _callSocketSystemName = null; 144 } else if (socket == _runSocket) { 145 _runSocketSystemName = null; 146 } else { 147 throw new IllegalArgumentException("unkown socket"); 148 } 149 } 150 151 @Override 152 public String getShortDescription(Locale locale) { 153 return Bundle.getMessage(locale, "ActionShutDownTask_Short"); 154 } 155 156 @Override 157 public String getLongDescription(Locale locale) { 158 return Bundle.getMessage(locale, "ActionShutDownTask_Long", _callSocket.getName(), _runSocket.getName()); 159 } 160 161 public FemaleDigitalExpressionSocket getCallSocket() { 162 return _callSocket; 163 } 164 165 public String getCallSocketSystemName() { 166 return _callSocketSystemName; 167 } 168 169 public void setCallSocketSystemName(String systemName) { 170 _callSocketSystemName = systemName; 171 } 172 173 public FemaleDigitalActionSocket getRunSocket() { 174 return _runSocket; 175 } 176 177 public String getRunSocketSystemName() { 178 return _runSocketSystemName; 179 } 180 181 public void setRunSocketSystemName(String systemName) { 182 _runSocketSystemName = systemName; 183 } 184 185 /** {@inheritDoc} */ 186 @Override 187 public void setup() { 188 try { 189 if (!_callSocket.isConnected() 190 || !_callSocket.getConnectedSocket().getSystemName() 191 .equals(_callSocketSystemName)) { 192 193 String socketSystemName = _callSocketSystemName; 194 195 _callSocket.disconnect(); 196 197 if (socketSystemName != null) { 198 MaleSocket maleSocket = 199 InstanceManager.getDefault(DigitalExpressionManager.class) 200 .getBySystemName(socketSystemName); 201 if (maleSocket != null) { 202 _callSocket.connect(maleSocket); 203 maleSocket.setup(); 204 } else { 205 log.error("cannot load digital expression {}", socketSystemName); 206 } 207 } 208 } else { 209 _callSocket.getConnectedSocket().setup(); 210 } 211 212 if (!_runSocket.isConnected() 213 || !_runSocket.getConnectedSocket().getSystemName() 214 .equals(_runSocketSystemName)) { 215 216 String socketSystemName = _runSocketSystemName; 217 218 _runSocket.disconnect(); 219 220 if (socketSystemName != null) { 221 MaleSocket maleSocket = 222 InstanceManager.getDefault(DigitalActionManager.class) 223 .getBySystemName(socketSystemName); 224 if (maleSocket != null) { 225 _runSocket.connect(maleSocket); 226 maleSocket.setup(); 227 } else { 228 log.error("cannot load digital action {}", socketSystemName); 229 } 230 } 231 } else { 232 _runSocket.getConnectedSocket().setup(); 233 } 234 } catch (SocketAlreadyConnectedException ex) { 235 // This shouldn't happen and is a runtime error if it does. 236 throw new RuntimeException("socket is already connected"); 237 } 238 } 239 240 241 242 private class InternalCallSocket 243 extends DefaultFemaleDigitalActionSocket { 244 245 private ConditionalNG conditionalNG; 246 private SymbolTable newSymbolTable; 247 private boolean _completed = false; 248 private boolean _result = true; // If no connected socket, return true 249 250 public InternalCallSocket() { 251 super(null, new FemaleSocketListener(){ 252 @Override 253 public void connected(FemaleSocket socket) { 254 // Do nothing 255 } 256 257 @Override 258 public void disconnected(FemaleSocket socket) { 259 // Do nothing 260 } 261 }, "E"); 262 } 263 264 @Override 265 public void execute() throws JmriException { 266 if (conditionalNG == null) { throw new NullPointerException("conditionalNG is null"); } 267 if (_callSocket != null) { 268 SymbolTable oldSymbolTable = conditionalNG.getSymbolTable(); 269 conditionalNG.setSymbolTable(newSymbolTable); 270 _result = _callSocket.evaluate(); 271 conditionalNG.setSymbolTable(oldSymbolTable); 272 synchronized(_lock) { 273 _completed = true; 274 _lock.notifyAll(); 275 } 276 } 277 } 278 279 } 280 281 282 283 private class InternalRunSocket 284 extends DefaultFemaleDigitalActionSocket { 285 286 private ConditionalNG conditionalNG; 287 private SymbolTable newSymbolTable; 288 private boolean _completed = false; 289 290 public InternalRunSocket() { 291 super(null, new FemaleSocketListener(){ 292 @Override 293 public void connected(FemaleSocket socket) { 294 // Do nothing 295 } 296 297 @Override 298 public void disconnected(FemaleSocket socket) { 299 // Do nothing 300 } 301 }, "A"); 302 } 303 304 @Override 305 public void execute() throws JmriException { 306 if (conditionalNG == null) { throw new NullPointerException("conditionalNG is null"); } 307 if (_runSocket != null) { 308 SymbolTable oldSymbolTable = conditionalNG.getSymbolTable(); 309 conditionalNG.setSymbolTable(newSymbolTable); 310 _runSocket.execute(); 311 conditionalNG.setSymbolTable(oldSymbolTable); 312 synchronized(_lock) { 313 _completed = true; 314 _lock.notifyAll(); 315 } 316 } 317 } 318 319 } 320 321 322 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ActionShutDownTask.class); 323 324}