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