001package jmri.jmrit.logixng.actions; 002 003import java.time.LocalTime; 004import java.time.format.DateTimeFormatter; 005import java.time.format.DateTimeParseException; 006import java.util.*; 007 008import jmri.*; 009import jmri.jmrit.logixng.*; 010import jmri.jmrit.logixng.util.LogixNG_SelectEnum; 011import jmri.jmrit.logixng.util.LogixNG_SelectInteger; 012import jmri.jmrit.logixng.util.parser.ParserException; 013import jmri.util.ThreadingUtil; 014 015/** 016 * This action provides the ability to set the fast clock time and start and stop the fast clock. 017 * 018 * @author Daniel Bergqvist Copyright 2021 019 * @author Dave Sand Copyright 2021 020 */ 021public class ActionClock extends AbstractDigitalAction { 022 023 private final LogixNG_SelectEnum<ClockState> _selectEnum = 024 new LogixNG_SelectEnum<>(this, ClockState.values(), ClockState.SetClock); 025 private final LogixNG_SelectInteger _selectValue = 026 new LogixNG_SelectInteger(this, new TimeFormatterParserValidator()); 027 028 029 public ActionClock(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 ActionClock copy = new ActionClock(sysName, userName); 041 copy.setComment(getComment()); 042 _selectEnum.copy(copy._selectEnum); 043 _selectValue.copy(copy._selectValue); 044 return manager.registerAction(copy); 045 } 046 047 public LogixNG_SelectEnum<ClockState> getSelectEnum() { 048 return _selectEnum; 049 } 050 051 public LogixNG_SelectInteger getSelectTime() { 052 return _selectValue; 053 } 054 055 /** 056 * Convert minutes since midnight to hh:mm. 057 * @param minutes The number of minutes from 0 to 1439. 058 * @return time formatted as hh:mm. 059 */ 060 public static String formatTime(int minutes) { 061 String hhmm = "00:00"; 062 if (minutes >= 0 && minutes < 1440) { 063 hhmm = String.format("%02d:%02d", 064 minutes / 60, 065 minutes % 60); 066 } 067 return hhmm; 068 } 069 070 /** {@inheritDoc} */ 071 @Override 072 public LogixNG_Category getCategory() { 073 return LogixNG_Category.ITEM; 074 } 075 076 /** {@inheritDoc} */ 077 @Override 078 public void execute() throws JmriException { 079 080 ClockState theState = _selectEnum.evaluateEnum(getConditionalNG()); 081 int theValue = _selectValue.evaluateValue(getConditionalNG()); 082 083 jmri.Timebase timebase = InstanceManager.getDefault(jmri.Timebase.class); 084 085 ThreadingUtil.runOnLayoutWithJmriException(() -> { 086 switch(theState) { 087 case SetClock: 088 Calendar cal = Calendar.getInstance(); 089 cal.setTime(timebase.getTime()); 090 cal.set(Calendar.HOUR_OF_DAY, theValue / 60); 091 cal.set(Calendar.MINUTE, theValue % 60); 092 cal.set(Calendar.SECOND, 0); 093 timebase.userSetTime(cal.getTime()); 094 break; 095 096 case StartClock: 097 timebase.setRun(true); 098 break; 099 100 case StopClock: 101 timebase.setRun(false); 102 break; 103 104 default: 105 throw new IllegalArgumentException("Invalid clock state: " + theState.name()); 106 } 107 }); 108 } 109 110 @Override 111 public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException { 112 throw new UnsupportedOperationException("Not supported."); 113 } 114 115 @Override 116 public int getChildCount() { 117 return 0; 118 } 119 120 @Override 121 public String getShortDescription(Locale locale) { 122 return Bundle.getMessage(locale, "ActionClock_Short"); 123 } 124 125 @Override 126 public String getLongDescription(Locale locale) { 127 String value; 128 if (_selectValue.isDirectAddressing()) { 129 value = formatTime(_selectValue.getValue()); 130 } else { 131 value = _selectValue.getDescription(locale); 132 } 133 if (_selectEnum.isDirectAddressing()) { 134 if (_selectEnum.getEnum() == ClockState.SetClock) { 135 return Bundle.getMessage(locale, "ActionClock_LongTime", _selectEnum.getDescription(locale), value); 136 } 137 return Bundle.getMessage(locale, "ActionClock_Long", _selectEnum.getDescription(locale), value); 138 } else { 139 return Bundle.getMessage(locale, "ActionClock_LongTimeIndirect", _selectEnum.getDescription(locale), value); 140 } 141 } 142 143 /** {@inheritDoc} */ 144 @Override 145 public void setup() { 146 // Do nothing 147 } 148 149 /** {@inheritDoc} */ 150 @Override 151 public void disposeMe() { 152 } 153 154 155 public enum ClockState { 156 SetClock(Bundle.getMessage("ActionClock_SetClock")), 157 StartClock(Bundle.getMessage("ActionClock_StartClock")), 158 StopClock(Bundle.getMessage("ActionClock_StopClock")); 159 160 private final String _text; 161 162 private ClockState(String text) { 163 this._text = text; 164 } 165 166 @Override 167 public String toString() { 168 return _text; 169 } 170 171 } 172 173 174 private static class TimeFormatterParserValidator 175 implements LogixNG_SelectInteger.FormatterParserValidator { 176 177 @Override 178 public int getInitialValue() { 179 return 0; 180 } 181 182 @Override 183 public String format(int value) { 184 return ActionClock.formatTime(value); 185 } 186 187 @Override 188 public int parse(String str) { 189 int minutes; 190 191 try { 192 minutes = Integer.parseInt(str); 193 if (minutes < 0 || minutes > 1439) { 194 return 0; 195 } 196 return minutes; 197 } catch (NumberFormatException e) { 198 // Do nothing 199 } 200 201 LocalTime newHHMM; 202 try { 203 newHHMM = LocalTime.parse(str.trim(), DateTimeFormatter.ofPattern("H:mm")); 204 minutes = newHHMM.getHour() * 60 + newHHMM.getMinute(); 205 if (minutes < 0 || minutes > 1439) { 206 return 0; 207 } 208 return minutes; 209 } catch (DateTimeParseException ex) { 210 return 0; 211 } 212 } 213 214 @Override 215 public String validate(String str) { 216 int minutes; 217 218 try { 219 minutes = Integer.parseInt(str); 220 if (minutes < 0 || minutes > 1439) { 221 return Bundle.getMessage("ActionClock_RangeError"); 222 } 223 return null; 224 } catch (NumberFormatException e) { 225 // Do nothing 226 } 227 228 LocalTime newHHMM; 229 try { 230 newHHMM = LocalTime.parse(str.trim(), DateTimeFormatter.ofPattern("H:mm")); 231 minutes = newHHMM.getHour() * 60 + newHHMM.getMinute(); 232 if (minutes < 0 || minutes > 1439) { 233 return Bundle.getMessage("ActionClock_RangeError"); 234 } 235 } catch (DateTimeParseException ex) { 236 return Bundle.getMessage("ActionClock_ParseError", ex.getParsedString()); 237 } 238 return null; 239 } 240 241 } 242 243// private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ActionPower.class); 244 245}