001package jmri.managers; 002 003import java.util.Enumeration; 004import java.util.Objects; 005import javax.annotation.Nonnull; 006import jmri.JmriException; 007import jmri.Manager; 008import jmri.Sensor; 009import jmri.SensorManager; 010import jmri.SystemConnectionMemo; 011 012/** 013 * Abstract base implementation of the SensorManager interface. 014 * 015 * @author Bob Jacobsen Copyright (C) 2001, 2003 016 */ 017public abstract class AbstractSensorManager extends AbstractManager<Sensor> implements SensorManager { 018 019 /** 020 * Create a new SensorManager instance. 021 * 022 * @param memo the system connection 023 */ 024 public AbstractSensorManager(SystemConnectionMemo memo) { 025 super(memo); 026 } 027 028 /** {@inheritDoc} */ 029 @Override 030 public int getXMLOrder() { 031 return Manager.SENSORS; 032 } 033 034 /** {@inheritDoc} */ 035 @Override 036 public char typeLetter() { 037 return 'S'; 038 } 039 040 /** {@inheritDoc} */ 041 @Override 042 @Nonnull 043 public Sensor provideSensor(@Nonnull String name) { 044 Sensor t = getSensor(name); 045 if (t == null) { 046 t = newSensor(makeSystemName(name), null); 047 } 048 return t; 049 } 050 051 /** {@inheritDoc} */ 052 @Override 053 public Sensor getSensor(@Nonnull String name) { 054 Sensor t = getByUserName(name); 055 if (t != null) { 056 return t; 057 } 058 return getBySystemName(name); 059 } 060 061 static final java.util.regex.Matcher numberMatcher = java.util.regex.Pattern.compile("\\d++").matcher(""); 062 063 boolean isNumber(@Nonnull String s) { 064 synchronized (numberMatcher) { 065 return numberMatcher.reset(s).matches(); 066 } 067 } 068 069 /** {@inheritDoc} 070 * Special handling for numeric argument, which is treated as the suffix of a new system name 071 */ 072 @Override 073 074 public Sensor getBySystemName(@Nonnull String key) { 075 if (isNumber(key)) { 076 key = makeSystemName(key); 077 } 078 return _tsys.get(key); 079 } 080 081 /** 082 * Create a New Sensor. 083 * {@inheritDoc} 084 */ 085 @Override 086 @Nonnull 087 public Sensor newSensor(@Nonnull String systemName, String userName) throws IllegalArgumentException { 088 log.debug(" newSensor(\"{}\", \"{}\")", systemName, (userName == null ? "null" : userName)); 089 Objects.requireNonNull(systemName, "SystemName cannot be null. UserName was " 090 + (userName == null ? "null" : userName)); // NOI18N 091 systemName = validateSystemNameFormat(systemName); 092 // return existing if there is one 093 Sensor s; 094 if (userName != null) { 095 s = getByUserName(userName); 096 if (s != null) { 097 if (getBySystemName(systemName) != s) { 098 log.error("inconsistent user ({}) and system name ({}) results; userName related to ({})", userName, systemName, s.getSystemName()); 099 } 100 return s; 101 } 102 } 103 s = getBySystemName(systemName); 104 if (s != null) { 105 if ((s.getUserName() == null) && (userName != null)) { 106 s.setUserName(userName); 107 } else if (userName != null) { 108 log.warn("Found sensor via system name ({}) with non-null user name ({}). Sensor \"{}({})\" cannot be used.", 109 systemName, s.getUserName(), systemName, userName); 110 } 111 return s; 112 } 113 // doesn't exist, make a new one 114 s = createNewSensor(systemName, userName); 115 // save in the maps 116 register(s); 117 118 return s; 119 } 120 121 /** {@inheritDoc} */ 122 @Override 123 @Nonnull 124 public String getBeanTypeHandled(boolean plural) { 125 return Bundle.getMessage(plural ? "BeanNameSensors" : "BeanNameSensor"); 126 } 127 128 /** 129 * {@inheritDoc} 130 */ 131 @Override 132 public Class<Sensor> getNamedBeanClass() { 133 return Sensor.class; 134 } 135 136 /** 137 * Internal method to invoke the factory and create a new Sensor. 138 * 139 * Called after all the logic for returning an existing Sensor 140 * has been invoked. 141 * An existing SystemName is not found, existing UserName not found. 142 * 143 * Implementing classes should base Sensor on the system name, then add user name. 144 * 145 * @param systemName the system name to use for the new Sensor 146 * @param userName the optional user name to use for the new Sensor 147 * @return the new Sensor 148 * @throws IllegalArgumentException if unsuccessful with reason for fail. 149 */ 150 @Nonnull 151 abstract protected Sensor createNewSensor(@Nonnull String systemName, String userName) throws IllegalArgumentException; 152 153 /** 154 * {@inheritDoc} 155 * Delay between requesting individual Sensor status is determined by the 156 * Connection Output Interval Setting. 157 */ 158 @Override 159 public void updateAll() { 160 int i = 0; 161 for ( Sensor nb : getNamedBeanSet() ) { 162 jmri.util.ThreadingUtil.runOnLayoutDelayed( nb::requestUpdateFromLayout, 163 i * getMemo().getOutputInterval() ); 164 i++; 165 } 166 } 167 168 /** 169 * Default Sensor ensures a numeric only system name. 170 * {@inheritDoc} 171 */ 172 @Nonnull 173 @Override 174 public String createSystemName(@Nonnull String curAddress, @Nonnull String prefix) throws JmriException { 175 return prefix + typeLetter() + checkNumeric(curAddress); 176 } 177 178 protected long sensorDebounceGoingActive = 0L; 179 protected long sensorDebounceGoingInActive = 0L; 180 181 /** {@inheritDoc} */ 182 @Override 183 public long getDefaultSensorDebounceGoingActive() { 184 return sensorDebounceGoingActive; 185 } 186 187 /** {@inheritDoc} */ 188 @Override 189 public long getDefaultSensorDebounceGoingInActive() { 190 return sensorDebounceGoingInActive; 191 } 192 193 /** {@inheritDoc} */ 194 @Override 195 public void setDefaultSensorDebounceGoingActive(long time) { 196 if (time == sensorDebounceGoingActive) { 197 return; 198 } 199 sensorDebounceGoingActive = time; 200 Enumeration<String> en = _tsys.keys(); 201 while (en.hasMoreElements()) { 202 Sensor sen = _tsys.get(en.nextElement()); 203 if (sen.getUseDefaultTimerSettings()) { 204 sen.setSensorDebounceGoingActiveTimer(time); 205 } 206 } 207 } 208 209 /** {@inheritDoc} */ 210 @Override 211 public void setDefaultSensorDebounceGoingInActive(long time) { 212 if (time == sensorDebounceGoingInActive) { 213 return; 214 } 215 sensorDebounceGoingInActive = time; 216 Enumeration<String> en = _tsys.keys(); 217 while (en.hasMoreElements()) { 218 Sensor sen = _tsys.get(en.nextElement()); 219 if (sen.getUseDefaultTimerSettings()) { 220 sen.setSensorDebounceGoingInActiveTimer(time); 221 } 222 } 223 } 224 225 /** 226 * {@inheritDoc} 227 * This default implementation always returns false. 228 * 229 * @return true if pull up/pull down configuration is supported. 230 */ 231 @Override 232 public boolean isPullResistanceConfigurable(){ 233 return false; 234 } 235 236 /** {@inheritDoc} */ 237 @Override 238 public String getEntryToolTip() { 239 return Bundle.getMessage("EnterNumber1to9999ToolTip"); 240 } 241 242 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractSensorManager.class); 243 244}