001package jmri.jmrit.logixng.actions; 002 003import java.util.Locale; 004import java.util.Map; 005 006import jmri.*; 007import jmri.jmrit.logixng.*; 008 009/** 010 * Runs an engine. 011 * This action reads an analog expression with the loco address and sets its 012 * speed according to an alaog expression and the direction according to a 013 * digital expression. 014 * 015 * @author Daniel Bergqvist Copyright 2019 016 */ 017public final class ActionThrottle extends AbstractDigitalAction 018 implements FemaleSocketListener { 019 020 public static final int LOCO_ADDRESS_SOCKET = 0; 021 public static final int LOCO_SPEED_SOCKET = LOCO_ADDRESS_SOCKET + 1; 022 public static final int LOCO_DIRECTION_SOCKET = LOCO_SPEED_SOCKET + 1; 023 public static final int LOCO_FUNCTION_SOCKET = LOCO_DIRECTION_SOCKET + 1; 024 public static final int LOCO_FUNCTION_ONOFF_SOCKET = LOCO_FUNCTION_SOCKET + 1; 025 public static final int NUM_LOCO_SOCKETS = LOCO_FUNCTION_ONOFF_SOCKET + 1; 026 027 private SystemConnectionMemo _memo; 028 private ThrottleManager _throttleManager; 029 private ThrottleManager _oldThrottleManager; 030 031 // The throttle if we have one or if a request is sent, null otherwise 032 private DccThrottle _throttle; 033 private ThrottleListener _throttleListener; 034 035 private String _locoAddressSocketSystemName; 036 private String _locoSpeedSocketSystemName; 037 private String _locoDirectionSocketSystemName; 038 private String _locoFunctionSocketSystemName; 039 private String _locoFunctionOnOffSocketSystemName; 040 private final FemaleAnalogExpressionSocket _locoAddressSocket; 041 private final FemaleAnalogExpressionSocket _locoSpeedSocket; 042 private final FemaleDigitalExpressionSocket _locoDirectionSocket; 043 private final FemaleAnalogExpressionSocket _locoFunctionSocket; 044 private final FemaleDigitalExpressionSocket _locoFunctionOnOffSocket; 045 private boolean _stopLocoWhenSwitchingLoco = true; 046 047 048 public ActionThrottle(String sys, String user) { 049 super(sys, user); 050 _locoAddressSocket = InstanceManager.getDefault(AnalogExpressionManager.class) 051 .createFemaleSocket(this, this, Bundle.getMessage("ActionThrottle_SocketName_Address")); 052 _locoSpeedSocket = InstanceManager.getDefault(AnalogExpressionManager.class) 053 .createFemaleSocket(this, this, Bundle.getMessage("ActionThrottle_SocketName_Speed")); 054 _locoDirectionSocket = InstanceManager.getDefault(DigitalExpressionManager.class) 055 .createFemaleSocket(this, this, Bundle.getMessage("ActionThrottle_SocketName_Direction")); 056 _locoFunctionSocket = InstanceManager.getDefault(AnalogExpressionManager.class) 057 .createFemaleSocket(this, this, Bundle.getMessage("ActionThrottle_SocketName_Function")); 058 _locoFunctionOnOffSocket = InstanceManager.getDefault(DigitalExpressionManager.class) 059 .createFemaleSocket(this, this, Bundle.getMessage("ActionThrottle_SocketName_FunctionOnOff")); 060 061 // Set the _throttleManager variable 062 setMemo(null); 063 } 064 065 @Override 066 public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws JmriException { 067 DigitalActionManager manager = InstanceManager.getDefault(DigitalActionManager.class); 068 String sysName = systemNames.get(getSystemName()); 069 String userName = userNames.get(getSystemName()); 070 if (sysName == null) sysName = manager.getAutoSystemName(); 071 ActionThrottle copy = new ActionThrottle(sysName, userName); 072 copy.setComment(getComment()); 073 copy.setMemo(_memo); 074 return manager.registerAction(copy).deepCopyChildren(this, systemNames, userNames); 075 } 076 077 public void setMemo(SystemConnectionMemo memo) { 078 assertListenersAreNotRegistered(log, "setMemo"); 079 _memo = memo; 080 if (_memo != null) { 081 _throttleManager = _memo.get(jmri.ThrottleManager.class); 082 if (_throttleManager == null) { 083 throw new IllegalArgumentException("Memo "+memo.getUserName()+" doesn't have a ThrottleManager"); 084 } 085 } else { 086 _throttleManager = InstanceManager.getDefault(ThrottleManager.class); 087 } 088 } 089 090 public SystemConnectionMemo getMemo() { 091 return _memo; 092 } 093 094 /** {@inheritDoc} */ 095 @Override 096 public Category getCategory() { 097 return Category.ITEM; 098 } 099 100 /** {@inheritDoc} */ 101 @Override 102 public void execute() throws JmriException { 103 104 int currentLocoAddress = -1; 105 int newLocoAddress = -1; 106 107 if (_throttle != null) { 108 currentLocoAddress = _throttle.getLocoAddress().getNumber(); 109 } 110 111 if (_locoAddressSocket.isConnected()) { 112 newLocoAddress = 113 (int) ((MaleAnalogExpressionSocket)_locoAddressSocket.getConnectedSocket()) 114 .evaluate(); 115 } 116 117 if (_throttleManager != _oldThrottleManager) { 118 currentLocoAddress = -1; // Force request of new throttle 119 _oldThrottleManager = _throttleManager; 120 } 121 122 if (newLocoAddress != currentLocoAddress) { 123 124 if (_throttle != null) { 125 if (_stopLocoWhenSwitchingLoco) { 126 // Stop the loco 127 _throttle.setSpeedSetting(0); 128 } 129 // Release the loco 130 _throttleManager.releaseThrottle(_throttle, _throttleListener); 131 _throttle = null; 132 } 133 134 if (newLocoAddress != -1) { 135 136 _throttleListener = new ThrottleListener() { 137 @Override 138 public void notifyThrottleFound(DccThrottle t) { 139 _throttle = t; 140 executeConditionalNG(); 141 } 142 143 @Override 144 public void notifyFailedThrottleRequest(LocoAddress address, String reason) { 145 log.warn("loco {} cannot be aquired", address.getNumber()); 146 } 147 148 @Override 149 public void notifyDecisionRequired(LocoAddress address, ThrottleListener.DecisionType question) { 150 log.warn("Loco {} cannot be aquired. Decision required.", address.getNumber()); 151 } 152 }; 153 154 boolean result = _throttleManager.requestThrottle(newLocoAddress, _throttleListener); 155 156 if (!result) { 157 log.warn("loco {} cannot be aquired", newLocoAddress); 158 } 159 } 160 161 } 162 163 // We have a throttle if _throttle is not null 164 if (_throttle != null) { 165 166 double speed = 0; 167 boolean isForward = true; 168 int function = 0; 169 boolean isFunctionOn = true; 170 171 if (_locoSpeedSocket.isConnected()) { 172 speed = 173 ((MaleAnalogExpressionSocket)_locoSpeedSocket.getConnectedSocket()) 174 .evaluate(); 175 } 176 177 if (_locoDirectionSocket.isConnected()) { 178 isForward = 179 ((MaleDigitalExpressionSocket)_locoDirectionSocket.getConnectedSocket()) 180 .evaluate(); 181 } 182 183 if (_locoFunctionSocket.isConnected()) { 184 function = (int) Math.round( 185 ((MaleAnalogExpressionSocket)_locoFunctionSocket.getConnectedSocket()) 186 .evaluate()); 187 } 188 189 if (_locoFunctionOnOffSocket.isConnected()) { 190 isFunctionOn = 191 ((MaleDigitalExpressionSocket)_locoFunctionOnOffSocket.getConnectedSocket()) 192 .evaluate(); 193 } 194 195 DccThrottle throttle = _throttle; 196 float spd = (float) speed; 197 boolean fwd = isForward; 198 int func = function; 199 boolean funcState = isFunctionOn; 200 jmri.util.ThreadingUtil.runOnLayoutWithJmriException(() -> { 201 if (_locoSpeedSocket.isConnected()) throttle.setSpeedSetting(spd); 202 if (_locoDirectionSocket.isConnected()) throttle.setIsForward(fwd); 203 if (_locoFunctionSocket.isConnected() && _locoFunctionOnOffSocket.isConnected()) { 204 throttle.setFunction(func, funcState); 205 } 206 }); 207 } 208 } 209 210 @Override 211 public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException { 212 switch (index) { 213 case LOCO_ADDRESS_SOCKET: 214 return _locoAddressSocket; 215 216 case LOCO_SPEED_SOCKET: 217 return _locoSpeedSocket; 218 219 case LOCO_DIRECTION_SOCKET: 220 return _locoDirectionSocket; 221 222 case LOCO_FUNCTION_SOCKET: 223 return _locoFunctionSocket; 224 225 case LOCO_FUNCTION_ONOFF_SOCKET: 226 return _locoFunctionOnOffSocket; 227 228 default: 229 throw new IllegalArgumentException( 230 String.format("index has invalid value: %d", index)); 231 } 232 } 233 234 @Override 235 public int getChildCount() { 236 return NUM_LOCO_SOCKETS; 237 } 238 239 @Override 240 public void connected(FemaleSocket socket) { 241 if (socket == _locoAddressSocket) { 242 _locoAddressSocketSystemName = socket.getConnectedSocket().getSystemName(); 243 executeConditionalNG(); 244 } else if (socket == _locoSpeedSocket) { 245 _locoSpeedSocketSystemName = socket.getConnectedSocket().getSystemName(); 246 executeConditionalNG(); 247 } else if (socket == _locoDirectionSocket) { 248 _locoDirectionSocketSystemName = socket.getConnectedSocket().getSystemName(); 249 executeConditionalNG(); 250 } else if (socket == _locoFunctionSocket) { 251 _locoFunctionSocketSystemName = socket.getConnectedSocket().getSystemName(); 252 executeConditionalNG(); 253 } else if (socket == _locoFunctionOnOffSocket) { 254 _locoFunctionOnOffSocketSystemName = socket.getConnectedSocket().getSystemName(); 255 executeConditionalNG(); 256 } else { 257 throw new IllegalArgumentException("unkown socket"); 258 } 259 } 260 261 @Override 262 public void disconnected(FemaleSocket socket) { 263 if (socket == _locoAddressSocket) { 264 if (_throttle != null) { 265 // Stop the loco 266 _throttle.setSpeedSetting(0); 267 // Release the loco 268 _throttleManager.releaseThrottle(_throttle, _throttleListener); 269 } 270 _locoAddressSocketSystemName = null; 271 executeConditionalNG(); 272 } else if (socket == _locoSpeedSocket) { 273 _locoSpeedSocketSystemName = null; 274 executeConditionalNG(); 275 } else if (socket == _locoDirectionSocket) { 276 _locoDirectionSocketSystemName = null; 277 executeConditionalNG(); 278 } else if (socket == _locoFunctionSocket) { 279 _locoFunctionSocketSystemName = null; 280 executeConditionalNG(); 281 } else if (socket == _locoFunctionOnOffSocket) { 282 _locoFunctionOnOffSocketSystemName = null; 283 executeConditionalNG(); 284 } else { 285 throw new IllegalArgumentException("unkown socket"); 286 } 287 } 288 289 private void executeConditionalNG() { 290 if (_listenersAreRegistered) { 291 ConditionalNG c = getConditionalNG(); 292 if (c != null) { 293 c.execute(); 294 } 295 } 296 } 297 298 @Override 299 public String getShortDescription(Locale locale) { 300 return Bundle.getMessage(locale, "ActionThrottle_Short"); 301 } 302 303 @Override 304 public String getLongDescription(Locale locale) { 305 if (_memo != null) { 306 return Bundle.getMessage(locale, "ActionThrottle_LongConnection", 307 _memo.getUserName()); 308 } else { 309 return Bundle.getMessage(locale, "ActionThrottle_Long"); 310 } 311 } 312 313 public FemaleAnalogExpressionSocket getLocoAddressSocket() { 314 return _locoAddressSocket; 315 } 316 317 public String getLocoAddressSocketSystemName() { 318 return _locoAddressSocketSystemName; 319 } 320 321 public void setLocoAddressSocketSystemName(String systemName) { 322 _locoAddressSocketSystemName = systemName; 323 } 324 325 public FemaleAnalogExpressionSocket getLocoSpeedSocket() { 326 return _locoSpeedSocket; 327 } 328 329 public String getLocoSpeedSocketSystemName() { 330 return _locoSpeedSocketSystemName; 331 } 332 333 public void setLocoSpeedSocketSystemName(String systemName) { 334 _locoSpeedSocketSystemName = systemName; 335 } 336 337 public FemaleDigitalExpressionSocket getLocoDirectionSocket() { 338 return _locoDirectionSocket; 339 } 340 341 public String getLocoDirectionSocketSystemName() { 342 return _locoDirectionSocketSystemName; 343 } 344 345 public void setLocoDirectionSocketSystemName(String systemName) { 346 _locoDirectionSocketSystemName = systemName; 347 } 348 349 public FemaleAnalogExpressionSocket getLocoFunctionSocket() { 350 return _locoFunctionSocket; 351 } 352 353 public String getLocoFunctionSocketSystemName() { 354 return _locoFunctionSocketSystemName; 355 } 356 357 public void setLocoFunctionSocketSystemName(String systemName) { 358 _locoFunctionSocketSystemName = systemName; 359 } 360 361 public FemaleDigitalExpressionSocket getLocoFunctionOnOffSocket() { 362 return _locoFunctionOnOffSocket; 363 } 364 365 public String getLocoFunctionOnOffSocketSystemName() { 366 return _locoFunctionOnOffSocketSystemName; 367 } 368 369 public void setLocoFunctionOnOffSocketSystemName(String systemName) { 370 _locoFunctionOnOffSocketSystemName = systemName; 371 } 372 373 /** {@inheritDoc} */ 374 @Override 375 public void setup() { 376 try { 377 if ( !_locoAddressSocket.isConnected() 378 || !_locoAddressSocket.getConnectedSocket().getSystemName() 379 .equals(_locoAddressSocketSystemName)) { 380 381 String socketSystemName = _locoAddressSocketSystemName; 382 _locoAddressSocket.disconnect(); 383 if (socketSystemName != null) { 384 MaleSocket maleSocket = 385 InstanceManager.getDefault(AnalogExpressionManager.class) 386 .getBySystemName(socketSystemName); 387 _locoAddressSocket.disconnect(); 388 if (maleSocket != null) { 389 _locoAddressSocket.connect(maleSocket); 390 maleSocket.setup(); 391 } else { 392 log.error("cannot load analog expression {}", socketSystemName); 393 } 394 } 395 } else { 396 _locoAddressSocket.getConnectedSocket().setup(); 397 } 398 399 if ( !_locoSpeedSocket.isConnected() 400 || !_locoSpeedSocket.getConnectedSocket().getSystemName() 401 .equals(_locoSpeedSocketSystemName)) { 402 403 String socketSystemName = _locoSpeedSocketSystemName; 404 _locoSpeedSocket.disconnect(); 405 if (socketSystemName != null) { 406 MaleSocket maleSocket = 407 InstanceManager.getDefault(AnalogExpressionManager.class) 408 .getBySystemName(socketSystemName); 409 _locoSpeedSocket.disconnect(); 410 if (maleSocket != null) { 411 _locoSpeedSocket.connect(maleSocket); 412 maleSocket.setup(); 413 } else { 414 log.error("cannot load analog expression {}", socketSystemName); 415 } 416 } 417 } else { 418 _locoSpeedSocket.getConnectedSocket().setup(); 419 } 420 421 if ( !_locoDirectionSocket.isConnected() 422 || !_locoDirectionSocket.getConnectedSocket().getSystemName() 423 .equals(_locoDirectionSocketSystemName)) { 424 425 String socketSystemName = _locoDirectionSocketSystemName; 426 _locoDirectionSocket.disconnect(); 427 if (socketSystemName != null) { 428 MaleSocket maleSocket = 429 InstanceManager.getDefault(DigitalExpressionManager.class) 430 .getBySystemName(socketSystemName); 431 _locoDirectionSocket.disconnect(); 432 if (maleSocket != null) { 433 _locoDirectionSocket.connect(maleSocket); 434 maleSocket.setup(); 435 } else { 436 log.error("cannot load digital expression {}", socketSystemName); 437 } 438 } 439 } else { 440 _locoDirectionSocket.getConnectedSocket().setup(); 441 } 442 443 if ( !_locoFunctionSocket.isConnected() 444 || !_locoFunctionSocket.getConnectedSocket().getSystemName() 445 .equals(_locoFunctionSocketSystemName)) { 446 447 String socketSystemName = _locoFunctionSocketSystemName; 448 _locoFunctionSocket.disconnect(); 449 if (socketSystemName != null) { 450 MaleSocket maleSocket = 451 InstanceManager.getDefault(AnalogExpressionManager.class) 452 .getBySystemName(socketSystemName); 453 _locoFunctionSocket.disconnect(); 454 if (maleSocket != null) { 455 _locoFunctionSocket.connect(maleSocket); 456 maleSocket.setup(); 457 } else { 458 log.error("cannot load analog expression {}", socketSystemName); 459 } 460 } 461 } else { 462 _locoFunctionSocket.getConnectedSocket().setup(); 463 } 464 465 if ( !_locoFunctionOnOffSocket.isConnected() 466 || !_locoFunctionOnOffSocket.getConnectedSocket().getSystemName() 467 .equals(_locoFunctionOnOffSocketSystemName)) { 468 469 String socketSystemName = _locoFunctionOnOffSocketSystemName; 470 _locoFunctionOnOffSocket.disconnect(); 471 if (socketSystemName != null) { 472 MaleSocket maleSocket = 473 InstanceManager.getDefault(DigitalExpressionManager.class) 474 .getBySystemName(socketSystemName); 475 _locoFunctionOnOffSocket.disconnect(); 476 if (maleSocket != null) { 477 _locoFunctionOnOffSocket.connect(maleSocket); 478 maleSocket.setup(); 479 } else { 480 log.error("cannot load digital expression {}", socketSystemName); 481 } 482 } 483 } else { 484 _locoFunctionOnOffSocket.getConnectedSocket().setup(); 485 } 486 } catch (SocketAlreadyConnectedException ex) { 487 // This shouldn't happen and is a runtime error if it does. 488 throw new RuntimeException("socket is already connected"); 489 } 490 } 491 492 public boolean isStopLocoWhenSwitchingLoco() { 493 return _stopLocoWhenSwitchingLoco; 494 } 495 496 public void setStopLocoWhenSwitchingLoco(boolean value) { 497 _stopLocoWhenSwitchingLoco = value; 498 } 499 500 /** {@inheritDoc} */ 501 @Override 502 public void registerListenersForThisClass() { 503 _listenersAreRegistered = true; 504 } 505 506 /** {@inheritDoc} */ 507 @Override 508 public void unregisterListenersForThisClass() { 509 _listenersAreRegistered = false; 510 } 511 512 /** {@inheritDoc} */ 513 @Override 514 public void disposeMe() { 515 if (_throttle != null) { 516 _throttleManager.releaseThrottle(_throttle, _throttleListener); 517 } 518 } 519 520 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ActionThrottle.class); 521 522}