001package jmri.jmrit.logixng.actions; 002 003import java.beans.PropertyChangeEvent; 004import java.beans.PropertyChangeListener; 005import java.util.*; 006 007import jmri.*; 008import jmri.jmrit.logixng.*; 009import jmri.jmrit.logixng.util.LogixNG_SelectEnum; 010import jmri.jmrit.logixng.util.LogixNG_SelectDouble; 011import jmri.jmrit.logixng.util.parser.ParserException; 012import static jmri.jmrit.simpleclock.SimpleTimebase.MAXIMUM_RATE; 013import static jmri.jmrit.simpleclock.SimpleTimebase.MINIMUM_RATE; 014import jmri.util.ThreadingUtil; 015 016/** 017 * This action provides the ability to set the fast clock speed. 018 * 019 * @author Daniel Bergqvist Copyright 2021 020 * @author Dave Sand Copyright 2021 021 * @author Daniel Bergqvist Copyright 2022 022 */ 023public class ActionClockRate extends AbstractDigitalAction 024 implements PropertyChangeListener { 025 026 private final LogixNG_SelectEnum<ClockState> _selectEnum = 027 new LogixNG_SelectEnum<>(this, ClockState.values(), ClockState.SetClockRate, this); 028 private final LogixNG_SelectDouble _selectSpeed = 029 new LogixNG_SelectDouble(this, 3, this, new DefaultFormatterParserValidator()); 030 031 032 public ActionClockRate(String sys, String user) 033 throws BadUserNameException, BadSystemNameException { 034 super(sys, user); 035 } 036 037 @Override 038 public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws ParserException { 039 DigitalActionManager manager = InstanceManager.getDefault(DigitalActionManager.class); 040 String sysName = systemNames.get(getSystemName()); 041 String userName = userNames.get(getSystemName()); 042 if (sysName == null) sysName = manager.getAutoSystemName(); 043 ActionClockRate copy = new ActionClockRate(sysName, userName); 044 copy.setComment(getComment()); 045 _selectEnum.copy(copy._selectEnum); 046 _selectSpeed.copy(copy._selectSpeed); 047 return manager.registerAction(copy); 048 } 049 050 public LogixNG_SelectEnum<ClockState> getSelectEnum() { 051 return _selectEnum; 052 } 053 054 public LogixNG_SelectDouble getSelectSpeed() { 055 return _selectSpeed; 056 } 057 058 /** 059 * Convert speed to a decimal string. 060 * @param speed The speed 061 * @return speed formatted as %1.3f 062 */ 063 public static String formatSpeed(double speed) { 064 return String.format("%1.3f", speed); 065 } 066 067 /** {@inheritDoc} */ 068 @Override 069 public Category getCategory() { 070 return Category.ITEM; 071 } 072 073 /** {@inheritDoc} */ 074 @Override 075 public void execute() throws JmriException { 076 077 ClockState theState = _selectEnum.evaluateEnum(getConditionalNG()); 078 double theValue = _selectSpeed.evaluateValue(getConditionalNG()); 079 080 jmri.Timebase timebase = InstanceManager.getDefault(jmri.Timebase.class); 081 082 ThreadingUtil.runOnLayoutWithJmriException(() -> { 083 switch(theState) { 084 case SetClockRate: 085 try { 086 timebase.userSetRate(theValue); 087 } catch (TimebaseRateException e) { 088 // Do nothing. This error is already logged as an error 089 } 090 break; 091 092 case IncreaseClockRate: 093 try { 094 timebase.userSetRate(timebase.userGetRate() + theValue); 095 } catch (TimebaseRateException e) { 096 // Do nothing. This error is already logged as an error 097 } 098 break; 099 100 case DecreaseClockRate: 101 try { 102 timebase.userSetRate(timebase.userGetRate() - theValue); 103 } catch (TimebaseRateException e) { 104 // Do nothing. This error is already logged as an error 105 } 106 break; 107 108 default: 109 throw new IllegalArgumentException("Invalid clock state: " + theState.name()); 110 } 111 }); 112 } 113 114 @Override 115 public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException { 116 throw new UnsupportedOperationException("Not supported."); 117 } 118 119 @Override 120 public int getChildCount() { 121 return 0; 122 } 123 124 @Override 125 public String getShortDescription(Locale locale) { 126 return Bundle.getMessage(locale, "ActionClockRate_Short"); 127 } 128 129 @Override 130 public String getLongDescription(Locale locale) { 131 String value; 132 if (_selectSpeed.isDirectAddressing()) { 133 value = formatSpeed(_selectSpeed.getValue()); 134 } else { 135 value = _selectSpeed.getDescription(locale); 136 } 137 if (_selectEnum.isDirectAddressing()) { 138 if (_selectEnum.getEnum() == ClockState.SetClockRate) { 139 return Bundle.getMessage(locale, "ActionClockRate_LongTo", _selectEnum.getDescription(locale), value); 140 } 141 return Bundle.getMessage(locale, "ActionClockRate_LongWith", _selectEnum.getDescription(locale), value); 142 } else { 143 return Bundle.getMessage(locale, "ActionClockRate_LongTo", _selectEnum.getDescription(locale), value); 144 } 145 } 146 147 /** {@inheritDoc} */ 148 @Override 149 public void setup() { 150 // Do nothing 151 } 152 153 /** {@inheritDoc} */ 154 @Override 155 public void registerListenersForThisClass() { 156 _selectEnum.registerListeners(); 157 _selectSpeed.registerListeners(); 158 } 159 160 /** {@inheritDoc} */ 161 @Override 162 public void unregisterListenersForThisClass() { 163 _selectEnum.unregisterListeners(); 164 _selectSpeed.unregisterListeners(); 165 } 166 167 /** {@inheritDoc} */ 168 @Override 169 public void propertyChange(PropertyChangeEvent evt) { 170 getConditionalNG().execute(); 171 } 172 173 /** {@inheritDoc} */ 174 @Override 175 public void disposeMe() { 176 } 177 178 179 public enum ClockState { 180 SetClockRate(Bundle.getMessage("ActionClockRate_SetClockRate")), 181 IncreaseClockRate(Bundle.getMessage("ActionClockRate_IncreaseClockRate")), 182 DecreaseClockRate(Bundle.getMessage("ActionClockRate_DecreaseClockRate")); 183 184 private final String _text; 185 186 private ClockState(String text) { 187 this._text = text; 188 } 189 190 @Override 191 public String toString() { 192 return _text; 193 } 194 195 } 196 197 198 private static class DefaultFormatterParserValidator 199 extends LogixNG_SelectDouble.DefaultFormatterParserValidator { 200 201 @Override 202 public double getInitialValue() { 203 return 1.0; 204 } 205 206 @Override 207 public double parse(String str) { 208 try { 209 double value = Double.parseDouble(str); 210 if (value < MINIMUM_RATE || value > MAXIMUM_RATE) { 211 return MINIMUM_RATE; 212 } 213 return value; 214 } catch (NumberFormatException ex) { 215 return MINIMUM_RATE; 216 } 217 } 218 219 @Override 220 public String validate(String str) { 221 try { 222 double value = Double.parseDouble(str); 223 if (value < MINIMUM_RATE || value > MAXIMUM_RATE) { 224 return Bundle.getMessage("ActionClockRate_RangeError", 225 MINIMUM_RATE, MAXIMUM_RATE); 226 } 227 return null; 228 } catch (NumberFormatException ex) { 229 return Bundle.getMessage("ActionClockRate_ParseError", str); 230 } 231 } 232 233 } 234 235// private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ActionPower.class); 236 237}