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 boolean _isActive = false; 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 // Stop the loco 126 _throttle.setSpeedSetting(0); 127 // Release the loco 128 _throttleManager.releaseThrottle(_throttle, _throttleListener); 129 _throttle = null; 130 } 131 132 if (newLocoAddress != -1) { 133 134 _throttleListener = new ThrottleListener() { 135 @Override 136 public void notifyThrottleFound(DccThrottle t) { 137 _throttle = t; 138 executeConditionalNG(); 139 } 140 141 @Override 142 public void notifyFailedThrottleRequest(LocoAddress address, String reason) { 143 log.warn("loco {} cannot be aquired", address.getNumber()); 144 } 145 146 @Override 147 public void notifyDecisionRequired(LocoAddress address, ThrottleListener.DecisionType question) { 148 log.warn("Loco {} cannot be aquired. Decision required.", address.getNumber()); 149 } 150 }; 151 152 boolean result = _throttleManager.requestThrottle(newLocoAddress, _throttleListener); 153 154 if (!result) { 155 log.warn("loco {} cannot be aquired", newLocoAddress); 156 } 157 } 158 159 } 160 161 // We have a throttle if _throttle is not null 162 if (_throttle != null) { 163 164 double speed = 0; 165 boolean isForward = true; 166 int function = 0; 167 boolean isFunctionOn = true; 168 169 if (_locoSpeedSocket.isConnected()) { 170 speed = 171 ((MaleAnalogExpressionSocket)_locoSpeedSocket.getConnectedSocket()) 172 .evaluate(); 173 } 174 175 if (_locoDirectionSocket.isConnected()) { 176 isForward = 177 ((MaleDigitalExpressionSocket)_locoDirectionSocket.getConnectedSocket()) 178 .evaluate(); 179 } 180 181 if (_locoFunctionSocket.isConnected()) { 182 function = (int) Math.round( 183 ((MaleAnalogExpressionSocket)_locoFunctionSocket.getConnectedSocket()) 184 .evaluate()); 185 } 186 187 if (_locoFunctionOnOffSocket.isConnected()) { 188 isFunctionOn = 189 ((MaleDigitalExpressionSocket)_locoFunctionOnOffSocket.getConnectedSocket()) 190 .evaluate(); 191 } 192 193 DccThrottle throttle = _throttle; 194 float spd = (float) speed; 195 boolean fwd = isForward; 196 int func = function; 197 boolean funcState = isFunctionOn; 198 jmri.util.ThreadingUtil.runOnLayoutWithJmriException(() -> { 199 if (_locoSpeedSocket.isConnected()) throttle.setSpeedSetting(spd); 200 if (_locoDirectionSocket.isConnected()) throttle.setIsForward(fwd); 201 if (_locoFunctionSocket.isConnected() && _locoFunctionOnOffSocket.isConnected()) { 202 throttle.setFunction(func, funcState); 203 } 204 }); 205 } 206 } 207 208 @Override 209 public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException { 210 switch (index) { 211 case LOCO_ADDRESS_SOCKET: 212 return _locoAddressSocket; 213 214 case LOCO_SPEED_SOCKET: 215 return _locoSpeedSocket; 216 217 case LOCO_DIRECTION_SOCKET: 218 return _locoDirectionSocket; 219 220 case LOCO_FUNCTION_SOCKET: 221 return _locoFunctionSocket; 222 223 case LOCO_FUNCTION_ONOFF_SOCKET: 224 return _locoFunctionOnOffSocket; 225 226 default: 227 throw new IllegalArgumentException( 228 String.format("index has invalid value: %d", index)); 229 } 230 } 231 232 @Override 233 public int getChildCount() { 234 return NUM_LOCO_SOCKETS; 235 } 236 237 @Override 238 public void connected(FemaleSocket socket) { 239 if (socket == _locoAddressSocket) { 240 _locoAddressSocketSystemName = socket.getConnectedSocket().getSystemName(); 241 executeConditionalNG(); 242 } else if (socket == _locoSpeedSocket) { 243 _locoSpeedSocketSystemName = socket.getConnectedSocket().getSystemName(); 244 executeConditionalNG(); 245 } else if (socket == _locoDirectionSocket) { 246 _locoDirectionSocketSystemName = socket.getConnectedSocket().getSystemName(); 247 executeConditionalNG(); 248 } else if (socket == _locoFunctionSocket) { 249 _locoFunctionSocketSystemName = socket.getConnectedSocket().getSystemName(); 250 executeConditionalNG(); 251 } else if (socket == _locoFunctionOnOffSocket) { 252 _locoFunctionOnOffSocketSystemName = socket.getConnectedSocket().getSystemName(); 253 executeConditionalNG(); 254 } else { 255 throw new IllegalArgumentException("unkown socket"); 256 } 257 } 258 259 @Override 260 public void disconnected(FemaleSocket socket) { 261 if (socket == _locoAddressSocket) { 262 if (_throttle != null) { 263 // Stop the loco 264 _throttle.setSpeedSetting(0); 265 // Release the loco 266 _throttleManager.releaseThrottle(_throttle, _throttleListener); 267 } 268 _locoAddressSocketSystemName = null; 269 executeConditionalNG(); 270 } else if (socket == _locoSpeedSocket) { 271 _locoSpeedSocketSystemName = null; 272 executeConditionalNG(); 273 } else if (socket == _locoDirectionSocket) { 274 _locoDirectionSocketSystemName = null; 275 executeConditionalNG(); 276 } else if (socket == _locoFunctionSocket) { 277 _locoFunctionSocketSystemName = null; 278 executeConditionalNG(); 279 } else if (socket == _locoFunctionOnOffSocket) { 280 _locoFunctionOnOffSocketSystemName = null; 281 executeConditionalNG(); 282 } else { 283 throw new IllegalArgumentException("unkown socket"); 284 } 285 } 286 287 private void executeConditionalNG() { 288 if (_listenersAreRegistered) { 289 ConditionalNG c = getConditionalNG(); 290 if (c != null) { 291 c.execute(); 292 } 293 } 294 } 295 296 @Override 297 public String getShortDescription(Locale locale) { 298 return Bundle.getMessage(locale, "ActionThrottle_Short"); 299 } 300 301 @Override 302 public String getLongDescription(Locale locale) { 303 if (_memo != null) { 304 return Bundle.getMessage(locale, "ActionThrottle_LongConnection", 305 _memo.getUserName()); 306 } else { 307 return Bundle.getMessage(locale, "ActionThrottle_Long"); 308 } 309 } 310 311 public FemaleAnalogExpressionSocket getLocoAddressSocket() { 312 return _locoAddressSocket; 313 } 314 315 public String getLocoAddressSocketSystemName() { 316 return _locoAddressSocketSystemName; 317 } 318 319 public void setLocoAddressSocketSystemName(String systemName) { 320 _locoAddressSocketSystemName = systemName; 321 } 322 323 public FemaleAnalogExpressionSocket getLocoSpeedSocket() { 324 return _locoSpeedSocket; 325 } 326 327 public String getLocoSpeedSocketSystemName() { 328 return _locoSpeedSocketSystemName; 329 } 330 331 public void setLocoSpeedSocketSystemName(String systemName) { 332 _locoSpeedSocketSystemName = systemName; 333 } 334 335 public FemaleDigitalExpressionSocket getLocoDirectionSocket() { 336 return _locoDirectionSocket; 337 } 338 339 public String getLocoDirectionSocketSystemName() { 340 return _locoDirectionSocketSystemName; 341 } 342 343 public void setLocoDirectionSocketSystemName(String systemName) { 344 _locoDirectionSocketSystemName = systemName; 345 } 346 347 public FemaleAnalogExpressionSocket getLocoFunctionSocket() { 348 return _locoFunctionSocket; 349 } 350 351 public String getLocoFunctionSocketSystemName() { 352 return _locoFunctionSocketSystemName; 353 } 354 355 public void setLocoFunctionSocketSystemName(String systemName) { 356 _locoFunctionSocketSystemName = systemName; 357 } 358 359 public FemaleDigitalExpressionSocket getLocoFunctionOnOffSocket() { 360 return _locoFunctionOnOffSocket; 361 } 362 363 public String getLocoFunctionOnOffSocketSystemName() { 364 return _locoFunctionOnOffSocketSystemName; 365 } 366 367 public void setLocoFunctionOnOffSocketSystemName(String systemName) { 368 _locoFunctionOnOffSocketSystemName = systemName; 369 } 370 371 /** {@inheritDoc} */ 372 @Override 373 public void setup() { 374 try { 375 if ( !_locoAddressSocket.isConnected() 376 || !_locoAddressSocket.getConnectedSocket().getSystemName() 377 .equals(_locoAddressSocketSystemName)) { 378 379 String socketSystemName = _locoAddressSocketSystemName; 380 _locoAddressSocket.disconnect(); 381 if (socketSystemName != null) { 382 MaleSocket maleSocket = 383 InstanceManager.getDefault(AnalogExpressionManager.class) 384 .getBySystemName(socketSystemName); 385 _locoAddressSocket.disconnect(); 386 if (maleSocket != null) { 387 _locoAddressSocket.connect(maleSocket); 388 maleSocket.setup(); 389 } else { 390 log.error("cannot load analog expression {}", socketSystemName); 391 } 392 } 393 } else { 394 _locoAddressSocket.getConnectedSocket().setup(); 395 } 396 397 if ( !_locoSpeedSocket.isConnected() 398 || !_locoSpeedSocket.getConnectedSocket().getSystemName() 399 .equals(_locoSpeedSocketSystemName)) { 400 401 String socketSystemName = _locoSpeedSocketSystemName; 402 _locoSpeedSocket.disconnect(); 403 if (socketSystemName != null) { 404 MaleSocket maleSocket = 405 InstanceManager.getDefault(AnalogExpressionManager.class) 406 .getBySystemName(socketSystemName); 407 _locoSpeedSocket.disconnect(); 408 if (maleSocket != null) { 409 _locoSpeedSocket.connect(maleSocket); 410 maleSocket.setup(); 411 } else { 412 log.error("cannot load analog expression {}", socketSystemName); 413 } 414 } 415 } else { 416 _locoSpeedSocket.getConnectedSocket().setup(); 417 } 418 419 if ( !_locoDirectionSocket.isConnected() 420 || !_locoDirectionSocket.getConnectedSocket().getSystemName() 421 .equals(_locoDirectionSocketSystemName)) { 422 423 String socketSystemName = _locoDirectionSocketSystemName; 424 _locoDirectionSocket.disconnect(); 425 if (socketSystemName != null) { 426 MaleSocket maleSocket = 427 InstanceManager.getDefault(DigitalExpressionManager.class) 428 .getBySystemName(socketSystemName); 429 _locoDirectionSocket.disconnect(); 430 if (maleSocket != null) { 431 _locoDirectionSocket.connect(maleSocket); 432 maleSocket.setup(); 433 } else { 434 log.error("cannot load digital expression {}", socketSystemName); 435 } 436 } 437 } else { 438 _locoDirectionSocket.getConnectedSocket().setup(); 439 } 440 441 if ( !_locoFunctionSocket.isConnected() 442 || !_locoFunctionSocket.getConnectedSocket().getSystemName() 443 .equals(_locoFunctionSocketSystemName)) { 444 445 String socketSystemName = _locoFunctionSocketSystemName; 446 _locoFunctionSocket.disconnect(); 447 if (socketSystemName != null) { 448 MaleSocket maleSocket = 449 InstanceManager.getDefault(AnalogExpressionManager.class) 450 .getBySystemName(socketSystemName); 451 _locoFunctionSocket.disconnect(); 452 if (maleSocket != null) { 453 _locoFunctionSocket.connect(maleSocket); 454 maleSocket.setup(); 455 } else { 456 log.error("cannot load analog expression {}", socketSystemName); 457 } 458 } 459 } else { 460 _locoFunctionSocket.getConnectedSocket().setup(); 461 } 462 463 if ( !_locoFunctionOnOffSocket.isConnected() 464 || !_locoFunctionOnOffSocket.getConnectedSocket().getSystemName() 465 .equals(_locoFunctionOnOffSocketSystemName)) { 466 467 String socketSystemName = _locoFunctionOnOffSocketSystemName; 468 _locoFunctionOnOffSocket.disconnect(); 469 if (socketSystemName != null) { 470 MaleSocket maleSocket = 471 InstanceManager.getDefault(DigitalExpressionManager.class) 472 .getBySystemName(socketSystemName); 473 _locoFunctionOnOffSocket.disconnect(); 474 if (maleSocket != null) { 475 _locoFunctionOnOffSocket.connect(maleSocket); 476 maleSocket.setup(); 477 } else { 478 log.error("cannot load digital expression {}", socketSystemName); 479 } 480 } 481 } else { 482 _locoFunctionOnOffSocket.getConnectedSocket().setup(); 483 } 484 } catch (SocketAlreadyConnectedException ex) { 485 // This shouldn't happen and is a runtime error if it does. 486 throw new RuntimeException("socket is already connected"); 487 } 488 } 489 490 /** {@inheritDoc} */ 491 @Override 492 public void registerListenersForThisClass() { 493 _listenersAreRegistered = true; 494 } 495 496 /** {@inheritDoc} */ 497 @Override 498 public void unregisterListenersForThisClass() { 499 _listenersAreRegistered = false; 500 } 501 502 /** {@inheritDoc} */ 503 @Override 504 public void disposeMe() { 505 if (_throttle != null) { 506 _throttleManager.releaseThrottle(_throttle, _throttleListener); 507 } 508 } 509 510 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ActionThrottle.class); 511 512}