001package jmri.jmrit.logixng.actions; 002 003import java.beans.PropertyChangeEvent; 004import java.beans.PropertyChangeListener; 005import java.util.Locale; 006import java.util.Map; 007 008import jmri.*; 009import jmri.jmrit.logixng.*; 010import jmri.jmrit.logixng.util.LogixNG_SelectEnum; 011import jmri.jmrit.logixng.util.LogixNG_SelectInteger; 012 013/** 014 * Sets a function on a throttle 015 * 016 * @author Daniel Bergqvist Copyright 2023 017 */ 018public final class ActionThrottleFunction extends AbstractDigitalAction 019 implements PropertyChangeListener { 020 021 private SystemConnectionMemo _memo; 022 private ThrottleManager _throttleManager; 023 private ThrottleManager _oldThrottleManager; 024 025 // The throttle if we have one or if a request is sent, null otherwise 026 private DccThrottle _throttle; 027 private ThrottleListener _throttleListener; 028 029 private final LogixNG_SelectInteger _selectAddress = new LogixNG_SelectInteger(this, this); 030 private final LogixNG_SelectInteger _selectFunction = new LogixNG_SelectInteger(this, this); 031 private final LogixNG_SelectEnum<FunctionState> _selectOnOff = 032 new LogixNG_SelectEnum<>(this, FunctionState.values(), FunctionState.On, this); 033 034 035 public ActionThrottleFunction(String sys, String user) { 036 super(sys, user); 037 038 // Set the _throttleManager variable 039 setMemo(null); 040 } 041 042 @Override 043 public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws JmriException { 044 DigitalActionManager manager = InstanceManager.getDefault(DigitalActionManager.class); 045 String sysName = systemNames.get(getSystemName()); 046 String userName = userNames.get(getSystemName()); 047 if (sysName == null) sysName = manager.getAutoSystemName(); 048 ActionThrottleFunction copy = new ActionThrottleFunction(sysName, userName); 049 copy.setComment(getComment()); 050 copy.setMemo(_memo); 051 _selectAddress.copy(copy._selectAddress); 052 _selectFunction.copy(copy._selectFunction); 053 _selectOnOff.copy(copy._selectOnOff); 054 return manager.registerAction(copy).deepCopyChildren(this, systemNames, userNames); 055 } 056 057 public LogixNG_SelectInteger getSelectAddress() { 058 return _selectAddress; 059 } 060 061 public LogixNG_SelectInteger getSelectFunction() { 062 return _selectFunction; 063 } 064 065 public LogixNG_SelectEnum<FunctionState> getSelectOnOff() { 066 return _selectOnOff; 067 } 068 069 public void setMemo(SystemConnectionMemo memo) { 070 assertListenersAreNotRegistered(log, "setMemo"); 071 _memo = memo; 072 if (_memo != null) { 073 _throttleManager = _memo.get(jmri.ThrottleManager.class); 074 if (_throttleManager == null) { 075 throw new IllegalArgumentException("Memo "+memo.getUserName()+" doesn't have a ThrottleManager"); 076 } 077 } else { 078 _throttleManager = InstanceManager.getDefault(ThrottleManager.class); 079 } 080 } 081 082 public SystemConnectionMemo getMemo() { 083 return _memo; 084 } 085 086 /** {@inheritDoc} */ 087 @Override 088 public Category getCategory() { 089 return Category.ITEM; 090 } 091 092 /** {@inheritDoc} */ 093 @Override 094 public void execute() throws JmriException { 095 096 ConditionalNG conditionalNG = this.getConditionalNG(); 097 098 int currentLocoAddress = -1; 099 int newLocoAddress = -1; 100 101 if (_throttle != null) { 102 currentLocoAddress = _throttle.getLocoAddress().getNumber(); 103 } 104 105 newLocoAddress = _selectAddress.evaluateValue(conditionalNG); 106 107 if (_throttleManager != _oldThrottleManager) { 108 currentLocoAddress = -1; // Force request of new throttle 109 _oldThrottleManager = _throttleManager; 110 } 111 112 if (newLocoAddress != currentLocoAddress) { 113 114 if (_throttle != null) { 115 // Release the loco 116 _throttleManager.releaseThrottle(_throttle, _throttleListener); 117 _throttle = null; 118 } 119 120 if (newLocoAddress != -1) { 121 122 _throttleListener = new ThrottleListener() { 123 @Override 124 public void notifyThrottleFound(DccThrottle t) { 125 _throttle = t; 126 executeConditionalNG(); 127 } 128 129 @Override 130 public void notifyFailedThrottleRequest(LocoAddress address, String reason) { 131 log.warn("loco {} cannot be aquired", address.getNumber()); 132 } 133 134 @Override 135 public void notifyDecisionRequired(LocoAddress address, ThrottleListener.DecisionType question) { 136 log.warn("Loco {} cannot be aquired. Decision required.", address.getNumber()); 137 } 138 }; 139 140 boolean result = _throttleManager.requestThrottle(newLocoAddress, _throttleListener); 141 142 if (!result) { 143 log.warn("loco {} cannot be aquired", newLocoAddress); 144 } 145 } 146 147 } 148 149 // We have a throttle if _throttle is not null 150 if (_throttle != null) { 151 152 int function = _selectFunction.evaluateValue(conditionalNG); 153 boolean isFunctionOn = _selectOnOff.evaluateEnum(conditionalNG)._value; 154 155 DccThrottle throttle = _throttle; 156 int func = function; 157 boolean funcState = isFunctionOn; 158 jmri.util.ThreadingUtil.runOnLayoutWithJmriException(() -> { 159 throttle.setFunction(func, funcState); 160 }); 161 } 162 } 163 164 private void executeConditionalNG() { 165 if (_listenersAreRegistered) { 166 ConditionalNG c = getConditionalNG(); 167 if (c != null) { 168 c.execute(); 169 } 170 } 171 } 172 173 @Override 174 public String getShortDescription(Locale locale) { 175 return Bundle.getMessage(locale, "ActionThrottleFunction_Short"); 176 } 177 178 @Override 179 public String getLongDescription(Locale locale) { 180 if (_memo != null) { 181 return Bundle.getMessage(locale, "ActionThrottleFunction_LongConnection", 182 _selectAddress.getDescription(locale), 183 _selectFunction.getDescription(locale), 184 _selectOnOff.getDescription(locale), 185 _memo.getUserName()); 186 } else { 187 return Bundle.getMessage(locale, "ActionThrottleFunction_Long", 188 _selectAddress.getDescription(locale), 189 _selectFunction.getDescription(locale), 190 _selectOnOff.getDescription(locale)); 191 } 192 } 193 194 /** {@inheritDoc} */ 195 @Override 196 public void setup() { 197 // Do nothing 198 } 199 200 /** {@inheritDoc} */ 201 @Override 202 public void registerListenersForThisClass() { 203 _listenersAreRegistered = true; 204 } 205 206 /** {@inheritDoc} */ 207 @Override 208 public void unregisterListenersForThisClass() { 209 _listenersAreRegistered = false; 210 } 211 212 /** {@inheritDoc} */ 213 @Override 214 public void propertyChange(PropertyChangeEvent evt) { 215 getConditionalNG().execute(); 216 } 217 218 /** {@inheritDoc} */ 219 @Override 220 public void disposeMe() { 221 if (_throttle != null) { 222 _throttleManager.releaseThrottle(_throttle, _throttleListener); 223 } 224 } 225 226 public enum FunctionState { 227 Off(false, Bundle.getMessage("StateOff")), 228 On(true, Bundle.getMessage("StateOn")); 229 230 private final boolean _value; 231 private final String _text; 232 233 private FunctionState(boolean value, String text) { 234 this._value = value; 235 this._text = text; 236 } 237 238 public boolean getValue() { 239 return _value; 240 } 241 242 @Override 243 public String toString() { 244 return _text; 245 } 246 247 } 248 249 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ActionThrottleFunction.class); 250 251}