001package jmri.jmrit.logixng.expressions; 002 003import java.beans.PropertyChangeEvent; 004import java.beans.PropertyChangeListener; 005import java.util.*; 006 007import jmri.*; 008import jmri.jmrit.logixng.*; 009import jmri.util.TimerUtil; 010 011/** 012 * This expression is a clock. 013 * 014 * @author Daniel Bergqvist Copyright 2020 015 * @author Dave Sand Copyright 2021 016 */ 017public class ExpressionClock extends AbstractDigitalExpression implements PropertyChangeListener { 018 019 private Is_IsNot_Enum _is_IsNot = Is_IsNot_Enum.Is; 020 private Type _type = Type.FastClock; 021 private Timebase _fastClock; 022 private int _beginTime = 0; 023 private int _endTime = 0; 024 025 TimerTask timerTask = null; 026 private int milisInAMinute = 60000; 027 028 029 public ExpressionClock(String sys, String user) { 030 super(sys, user); 031 } 032 033 @Override 034 public Base getDeepCopy(Map<String, String> systemNames, Map<String, String> userNames) throws JmriException { 035 DigitalExpressionManager manager = InstanceManager.getDefault(DigitalExpressionManager.class); 036 String sysName = systemNames.get(getSystemName()); 037 String userName = userNames.get(getSystemName()); 038 if (sysName == null) sysName = manager.getAutoSystemName(); 039 ExpressionClock copy = new ExpressionClock(sysName, userName); 040 copy.setComment(getComment()); 041 copy.set_Is_IsNot(_is_IsNot); 042 copy.setType(_type); 043 copy.setRange(_beginTime, _endTime); 044 return manager.registerExpression(copy).deepCopyChildren(this, systemNames, userNames); 045 } 046 047 /** {@inheritDoc} */ 048 @Override 049 public Category getCategory() { 050 return Category.ITEM; 051 } 052 053 public void set_Is_IsNot(Is_IsNot_Enum is_IsNot) { 054 _is_IsNot = is_IsNot; 055 } 056 057 public Is_IsNot_Enum get_Is_IsNot() { 058 return _is_IsNot; 059 } 060 061 public void setType(Type type) { 062 assertListenersAreNotRegistered(log, "setType"); 063 _type = type; 064 065 if (_type == Type.FastClock) { 066 _fastClock = InstanceManager.getDefault(jmri.Timebase.class); 067 } else { 068 _fastClock = null; 069 } 070 } 071 072 public Type getType() { 073 return _type; 074 } 075 076 public void setRange(int beginTime, int endTime) { 077 assertListenersAreNotRegistered(log, "setRange"); 078 _beginTime = beginTime; 079 _endTime = endTime; 080 } 081 082 public int getBeginTime() { 083 return _beginTime; 084 } 085 086 public int getEndTime() { 087 return _endTime; 088 } 089 090 /** 091 * Convert minutes since midnight to hh:mm. 092 * @param minutes The number of minutes from 0 to 1439. 093 * @return time formatted as hh:mm. 094 */ 095 public static String formatTime(int minutes) { 096 String hhmm = "00:00"; 097 if (minutes >= 0 && minutes < 1440) { 098 hhmm = String.format("%02d:%02d", 099 minutes / 60, 100 minutes % 60); 101 } 102 return hhmm; 103 } 104 105 /** {@inheritDoc} */ 106 @Override 107 public boolean evaluate() { 108 boolean result; 109 110 Calendar currentTime = null; 111 112 switch (_type) { 113 case SystemClock: 114 currentTime = Calendar.getInstance(); 115 break; 116 117 case FastClock: 118 if (_fastClock == null) return false; 119 currentTime = Calendar.getInstance(); 120 currentTime.setTime(_fastClock.getTime()); 121 break; 122 123 default: 124 throw new UnsupportedOperationException("_type has unknown value: " + _type.name()); 125 } 126 127 int currentMinutes = (currentTime.get(Calendar.HOUR_OF_DAY) * 60) + currentTime.get(Calendar.MINUTE); 128 // check if current time is within range specified 129 if (_beginTime <= _endTime) { 130 // range is entirely within one day 131 result = (_beginTime <= currentMinutes) && (currentMinutes <= _endTime); 132 } else { 133 // range includes midnight 134 result = _beginTime <= currentMinutes || currentMinutes <= _endTime; 135 } 136 137 if (_is_IsNot == Is_IsNot_Enum.Is) { 138 return result; 139 } else { 140 return !result; 141 } 142 } 143 144 @Override 145 public FemaleSocket getChild(int index) throws IllegalArgumentException, UnsupportedOperationException { 146 throw new UnsupportedOperationException("Not supported."); 147 } 148 149 @Override 150 public int getChildCount() { 151 return 0; 152 } 153 154 @Override 155 public String getShortDescription(Locale locale) { 156 return Bundle.getMessage(locale, "Clock_Short"); 157 } 158 159 @Override 160 public String getLongDescription(Locale locale) { 161 switch (_type) { 162 case SystemClock: 163 return Bundle.getMessage(locale, "Clock_Long_SystemClock", _is_IsNot.toString(), 164 ExpressionClock.formatTime(_beginTime), 165 ExpressionClock.formatTime(_endTime)); 166 167 case FastClock: 168 return Bundle.getMessage(locale, "Clock_Long_FastClock", _is_IsNot.toString(), 169 ExpressionClock.formatTime(_beginTime), 170 ExpressionClock.formatTime(_endTime)); 171 172 default: 173 throw new RuntimeException("Unknown value of _timerType: "+_type.name()); 174 } 175 } 176 177 /** {@inheritDoc} */ 178 @Override 179 public void setup() { 180 // Do nothing 181 } 182 183 /** {@inheritDoc} 184 * The SystemClock listener creates a timer on the first call. Subsequent calls 185 * enabled timer processing. 186 */ 187 @Override 188 public void registerListenersForThisClass() { 189 if (!_listenersAreRegistered) { 190 switch (_type) { 191 case SystemClock: 192 scheduleTimer(); 193 break; 194 195 case FastClock: 196 _fastClock.addPropertyChangeListener("time", this); 197 break; 198 199 default: 200 throw new UnsupportedOperationException("_type has unknown value: " + _type.name()); 201 } 202 203 _listenersAreRegistered = true; 204 } 205 } 206 207 /** {@inheritDoc} 208 * The SystemClock timer flag is set false to suspend processing of timer events. The 209 * timer keeps running for the duration of the JMRI session. 210 */ 211 @Override 212 public void unregisterListenersForThisClass() { 213 if (_listenersAreRegistered) { 214 switch (_type) { 215 case SystemClock: 216 if (timerTask != null) timerTask.cancel(); 217 break; 218 219 case FastClock: 220 if (_fastClock != null) _fastClock.removePropertyChangeListener("time", this); 221 break; 222 223 default: 224 throw new UnsupportedOperationException("_type has unknown value: " + _type.name()); 225 } 226 227 _listenersAreRegistered = false; 228 } 229 } 230 231 private void scheduleTimer() { 232 timerTask = new TimerTask() { 233 @Override 234 public void run() { 235 propertyChange(null); 236 } 237 }; 238 TimerUtil.schedule(timerTask, System.currentTimeMillis() % milisInAMinute, milisInAMinute); 239 } 240 241 /** {@inheritDoc} */ 242 @Override 243 public void propertyChange(PropertyChangeEvent evt) { 244 getConditionalNG().execute(); 245 } 246 247 /** {@inheritDoc} */ 248 @Override 249 public void disposeMe() { 250 if (timerTask != null) timerTask.cancel(); 251 } 252 253 public enum Type { 254 FastClock(Bundle.getMessage("ClockTypeFastClock")), 255 SystemClock(Bundle.getMessage("ClockTypeSystemClock")); 256 257 private final String _text; 258 259 private Type(String text) { 260 this._text = text; 261 } 262 263 @Override 264 public String toString() { 265 return _text; 266 } 267 268 } 269 270 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ExpressionClock.class); 271 272}