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 an I18N decimal string. 060 * @param locale The Locale to use for the String conversion. 061 * @param speed The speed 062 * @return speed formatted as %1.3f 063 */ 064 public static String formatSpeed(Locale locale, double speed) { 065 return String.format(locale,"%1.3f", speed); 066 } 067 068 /** {@inheritDoc} */ 069 @Override 070 public Category getCategory() { 071 return Category.ITEM; 072 } 073 074 /** {@inheritDoc} */ 075 @Override 076 public void execute() throws JmriException { 077 078 ClockState theState = _selectEnum.evaluateEnum(getConditionalNG()); 079 double theValue = _selectSpeed.evaluateValue(getConditionalNG()); 080 081 jmri.Timebase timebase = InstanceManager.getDefault(jmri.Timebase.class); 082 083 ThreadingUtil.runOnLayoutWithJmriException(() -> { 084 switch(theState) { 085 case SetClockRate: 086 try { 087 timebase.userSetRate(theValue); 088 } catch (TimebaseRateException e) { 089 // Do nothing. This error is already logged as an error 090 } 091 break; 092 093 case IncreaseClockRate: 094 try { 095 timebase.userSetRate(timebase.userGetRate() + theValue); 096 } catch (TimebaseRateException e) { 097 // Do nothing. This error is already logged as an error 098 } 099 break; 100 101 case DecreaseClockRate: 102 try { 103 timebase.userSetRate(timebase.userGetRate() - theValue); 104 } catch (TimebaseRateException e) { 105 // Do nothing. This error is already logged as an error 106 } 107 break; 108 109 default: 110 throw new IllegalArgumentException("Invalid clock state: " + theState.name()); 111 } 112 }); 113 } 114 115 @Override 116 public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException { 117 throw new UnsupportedOperationException("Not supported."); 118 } 119 120 @Override 121 public int getChildCount() { 122 return 0; 123 } 124 125 @Override 126 public String getShortDescription(Locale locale) { 127 return Bundle.getMessage(locale, "ActionClockRate_Short"); 128 } 129 130 @Override 131 public String getLongDescription(Locale locale) { 132 String value; 133 if (_selectSpeed.isDirectAddressing()) { 134 value = formatSpeed( locale, _selectSpeed.getValue()); 135 } else { 136 value = _selectSpeed.getDescription(locale); 137 } 138 if (_selectEnum.isDirectAddressing()) { 139 if (_selectEnum.getEnum() == ClockState.SetClockRate) { 140 return Bundle.getMessage(locale, "ActionClockRate_LongTo", _selectEnum.getDescription(locale), value); 141 } 142 return Bundle.getMessage(locale, "ActionClockRate_LongWith", _selectEnum.getDescription(locale), value); 143 } else { 144 return Bundle.getMessage(locale, "ActionClockRate_LongTo", _selectEnum.getDescription(locale), value); 145 } 146 } 147 148 /** {@inheritDoc} */ 149 @Override 150 public void setup() { 151 // Do nothing 152 } 153 154 /** {@inheritDoc} */ 155 @Override 156 public void registerListenersForThisClass() { 157 _selectEnum.registerListeners(); 158 _selectSpeed.registerListeners(); 159 } 160 161 /** {@inheritDoc} */ 162 @Override 163 public void unregisterListenersForThisClass() { 164 _selectEnum.unregisterListeners(); 165 _selectSpeed.unregisterListeners(); 166 } 167 168 /** {@inheritDoc} */ 169 @Override 170 public void propertyChange(PropertyChangeEvent evt) { 171 getConditionalNG().execute(); 172 } 173 174 /** {@inheritDoc} */ 175 @Override 176 public void disposeMe() { 177 } 178 179 180 public enum ClockState { 181 SetClockRate(Bundle.getMessage("ActionClockRate_SetClockRate")), 182 IncreaseClockRate(Bundle.getMessage("ActionClockRate_IncreaseClockRate")), 183 DecreaseClockRate(Bundle.getMessage("ActionClockRate_DecreaseClockRate")); 184 185 private final String _text; 186 187 private ClockState(String text) { 188 this._text = text; 189 } 190 191 @Override 192 public String toString() { 193 return _text; 194 } 195 196 } 197 198 199 private static class DefaultFormatterParserValidator 200 extends LogixNG_SelectDouble.DefaultFormatterParserValidator { 201 202 @Override 203 public double getInitialValue() { 204 return 1.0; 205 } 206 207 @Override 208 public double parse(String str) { 209 try { 210 double value = Double.parseDouble(str); 211 if (value < MINIMUM_RATE || value > MAXIMUM_RATE) { 212 return MINIMUM_RATE; 213 } 214 return value; 215 } catch (NumberFormatException ex) { 216 return MINIMUM_RATE; 217 } 218 } 219 220 @Override 221 public String validate(String str) { 222 try { 223 double value = Double.parseDouble(str); 224 if (value < MINIMUM_RATE || value > MAXIMUM_RATE) { 225 return Bundle.getMessage("ActionClockRate_RangeError", 226 MINIMUM_RATE, MAXIMUM_RATE); 227 } 228 return null; 229 } catch (NumberFormatException ex) { 230 return Bundle.getMessage("ActionClockRate_ParseError", str); 231 } 232 } 233 234 } 235 236// private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ActionPower.class); 237 238}