001package jmri.jmrix.anyma; 002 003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 004 005import java.util.Comparator; 006import java.util.ResourceBundle; 007 008import javax.annotation.Nonnull; 009 010import jmri.*; 011import jmri.Manager.NameValidity; 012import jmri.jmrix.ConfiguringSystemConnectionMemo; 013import jmri.jmrix.DefaultSystemConnectionMemo; 014import jmri.util.NamedBeanComparator; 015 016import org.slf4j.Logger; 017import org.slf4j.LoggerFactory; 018 019/** 020 * Minimal SystemConnectionMemo for anyma dmx systems. 021 * 022 * @author George Warner Copyright (c) 2017-2018 023 * @since 4.9.6 024 */ 025public class AnymaDMX_SystemConnectionMemo extends DefaultSystemConnectionMemo implements ConfiguringSystemConnectionMemo { 026 027 private boolean configured = false; 028 029 /** 030 * constructor 031 */ 032 public AnymaDMX_SystemConnectionMemo() { 033 this("D", AnymaDMX_ConnectionTypeList.ANYMA_DMX); // default to "D" prefix 034 log.debug("* Constructor()"); 035 } 036 037 /** 038 * constructor. 039 * @param prefix system prefix. 040 * @param userName system username. 041 */ 042 public AnymaDMX_SystemConnectionMemo(@Nonnull String prefix, @Nonnull String userName) { 043 super(prefix, userName); 044 045 log.debug("* Constructor ({}, {})", prefix, userName); 046 047 register(); // registers general type 048 InstanceManager.store(this, AnymaDMX_SystemConnectionMemo.class); // also register as specific type 049 } 050 051 private AnymaDMX_TrafficController trafficController = null; 052 053 /** 054 * get the traffic controller 055 * 056 * @return the traffic controller 057 */ 058 protected AnymaDMX_TrafficController getTrafficController() { 059 return trafficController; 060 } 061 062 /** 063 * set the traffic controller 064 * 065 * @param trafficController the traffic controller 066 */ 067 protected void setTrafficController(AnymaDMX_TrafficController trafficController) { 068 this.trafficController = trafficController; 069 } 070 071 /** 072 * public method to get the user name for a valid system name 073 * 074 * @param systemName the system name 075 * @return "" (null string) if system name is not valid or does not exist 076 */ 077 public String getUserNameFromSystemName(String systemName) { 078 log.debug("* getUserNameFromSystemName('{}')", systemName); 079 String result = ""; // not any known light 080 int offset = checkSystemPrefix(systemName); 081 if (offset > 0) { 082 if (systemName.length() > offset) { 083 if (systemName.charAt(offset) == 'L') { 084 Light lgt = null; 085 lgt = InstanceManager.lightManagerInstance().getBySystemName(systemName); 086 if (lgt != null) { 087 result = lgt.getUserName(); 088 } 089 } 090 } 091 } 092 return result; 093 } 094 095 /** 096 * Public static method to parse a anyma dmx system name and return the 097 * channel number. Notes: 098 * <ul> 099 * <li>Channels are numbered from 1 to 512.</li> 100 * <li>Does not check whether that node is defined on current system.</li> 101 * </ul> 102 * @param systemName system name. 103 * @return 0 if an error is found. 104 */ 105 public int getChannelFromSystemName(String systemName) { 106 int result = 0; 107 log.debug("* getChannelFromSystemName('{}')", systemName); 108 109 int offset = checkSystemPrefix(systemName); 110 if (offset > 0) { 111 if (validSystemNameFormat(systemName, systemName.charAt(offset)) == NameValidity.VALID) { 112 // Find the beginning of the channel number field 113 int k = 0; 114 for (int i = offset; i < systemName.length(); i++) { 115 if (systemName.charAt(i) == 'L') { 116 k = i + 1; 117 break; 118 } 119 } 120 if (k > offset) { // k = position of "L" char in name 121 try { 122 result = Integer.parseInt(systemName.substring(k)); 123 } catch (NumberFormatException e) { 124 log.warn("invalid character in channel number field of anyma dmx system name: {}", systemName); 125 } 126 } 127 } else { 128 log.error("No point in normalizing if a valid system name format is not present"); 129 } 130 } else { 131 log.error("invalid system prefix in anyma dmx system name in getChannelFromSystemName: {}", systemName); 132 } 133 return result; 134 } 135 136 /** 137 * Public static method to check and skip the System Prefix string on a 138 * system name. 139 * 140 * @param systemName system name string. 141 * @return offset of the 1st character past the prefix, or -1 if not valid 142 * for this connection 143 */ 144 public int checkSystemPrefix(String systemName) { 145 log.debug("* checkSystemPrefix('{}')", systemName); 146 int result = -1; 147 if (systemName.startsWith(getSystemPrefix())) { 148 result = getSystemPrefix().length(); 149 } 150 return result; 151 } 152 153 /** 154 * Public static method to convert one format anyma dmx system name to the 155 * alternate format. 156 * 157 * @param systemName system name string. 158 * @return "" (empty string) if the supplied system name does not have a 159 * valid format, or if there is no representation in the alternate 160 * naming scheme 161 */ 162 public String convertSystemNameToAlternate(String systemName) { 163 log.debug("* convertSystemNameToAlternate('{}')", systemName); 164 String result = ""; 165 166 int offset = checkSystemPrefix(systemName); 167 if (offset > 0) { 168 if (validSystemNameFormat(systemName, systemName.charAt(offset)) == NameValidity.VALID) { 169 int channelNum = Integer.parseInt(systemName.substring(offset + 1)); 170 result = systemName.substring(0, offset + 1) + Integer.toString(channelNum); 171 } else { 172 log.error("valid system name format not present in anyma dmx system name: {}", systemName); 173 } 174 } else { 175 log.error("invalid system prefix in anyma dmx system name in convertSystemNameToAlternate: {}", systemName); 176 } 177 return result; 178 } 179 180 /** 181 * Public static method to validate system name format. 182 * Does not check whether that node is defined on current system. 183 * 184 * @param systemName proposed system name. 185 * @param type bean type, only L supported. 186 * @return enum indicating current validity, which might be just as a prefix 187 */ 188 public NameValidity validSystemNameFormat(@Nonnull String systemName, char type) { 189 log.debug("* validSystemNameFormat('{}', '{}')", systemName, type); 190 NameValidity result = NameValidity.INVALID; // assume failure (pessimist!) 191 192 int offset = checkSystemPrefix(systemName); 193 if (offset > 0) { 194 if (systemName.charAt(offset) == type) { 195 // This is a CLnnnxxx pattern address 196 int num; 197 try { 198 num = Integer.parseInt(systemName.substring(offset + 1)); 199 if ((num >= 1) && (num <= 512)) { 200 result = NameValidity.VALID; 201 } else { 202 log.debug("number field out of range in anyma dmx system name: {}", systemName); 203 } 204 } catch (NumberFormatException e) { 205 log.debug("invalid character in number field of anyma dmx system name: {}", systemName); 206 } 207 } else { 208 log.error("invalid type character in anyma dmx system name: {}", systemName); 209 } 210 } else { 211 log.error("invalid system prefix in anyma dmx system name in validSystemNameFormat: {}", systemName); 212 } 213 return result; 214 } 215 216 /** 217 * Public static method to validate anyma dmx system name for configuration. 218 * Does validate node number and system prefix. 219 * 220 * @param systemName anya dmx systemName. 221 * @param type bean type, only L supported. 222 * @return 'true' if system name has a valid meaning in current 223 * configuration, else returns 'false'. 224 */ 225 public boolean validSystemNameConfig(String systemName, char type) { 226 log.debug("* validSystemNameConfig('{}', '{}')", systemName, type); 227 boolean result = false; // assume failure (pessimist!) 228 if (validSystemNameFormat(systemName, type) == NameValidity.VALID) { 229 if (type == 'L') { 230 int channel = getChannelFromSystemName(systemName); 231 if ((channel >= 1) && (channel <= 512)) { 232 result = true; // The channel is valid 233 } 234 } else { 235 log.error("Invalid type specification in validSystemNameConfig call"); 236 } 237 } else { 238 log.error("valid system name format is not present"); 239 } 240 return result; 241 } 242 243 /** 244 * Public static method to parse a anyma dmx system name and return the Usb 245 * Node Address 246 * <p> 247 * Nodes are numbered from 0 - 127. Does not check whether that node is 248 * defined on current system. 249 * 250 * @param systemName system name. 251 * @return '-1' if invalid systemName format or if the node is not found. 252 */ 253 public int getNodeAddressFromSystemName(String systemName) { 254 int result = -1; // assume failure (pessimist!) 255 log.debug("* getNodeAddressFromSystemName('{}')", systemName); 256 int offset = checkSystemPrefix(systemName); 257 if (offset > 0) { 258 if (systemName.charAt(offset) == 'L') { 259 int num = Integer.parseInt(systemName.substring(offset + 1)); 260 if (num > 0) { 261 result = num; 262 } else { 263 log.warn("invalid anyma dmx system name: {}", systemName); 264 } 265 } else { 266 log.error("invalid character in header field of system name: {}", systemName); 267 } 268 } 269 return result; 270 } 271 272 /** 273 * {@inheritDoc} 274 */ 275 @Override 276 @SuppressFBWarnings(value = "OVERRIDING_METHODS_MUST_INVOKE_SUPER", 277 justification = "This system connection doesn't support ConsistManager from the super class") 278 public boolean provides(Class<?> c) { 279 return (get(c) != null); 280 } 281 282 /** 283 * {@inheritDoc} 284 */ 285 @Override 286 @SuppressWarnings("unchecked") 287 @SuppressFBWarnings(value = "OVERRIDING_METHODS_MUST_INVOKE_SUPER", 288 justification = "This system connection doesn't support ConsistManager from the super class") 289 public <T> T get(Class<T> T) { 290 T result = null; // nothing by default 291 log.debug("* get({})", T.toString()); 292 if (!getDisabled()) { 293 if (!configured) { 294 configureManagers(); 295 } 296 if (T.equals(LightManager.class)) { 297 result = (T) getLightManager(); 298 } 299 } 300 return result; 301 } 302 303 /** 304 * Configure the common managers for anyma dmx connections. This puts the 305 * common manager config in one place. 306 */ 307 @Override 308 public void configureManagers() { 309 log.debug("* configureManagers()"); 310 InstanceManager.setLightManager(getLightManager()); 311 312 if (configured) { 313 log.warn("calling configureManagers for a second time", new Exception("traceback")); 314 } 315 configured = true; 316 } 317 318 /** 319 * get the LightManager 320 * 321 * @return the LightManager 322 */ 323 public UsbLightManager getLightManager() { 324 log.debug("* getLightManager()"); 325 if (getDisabled()) { 326 return null; 327 } 328 return (UsbLightManager) classObjectMap.computeIfAbsent(LightManager.class, (Class<?> c) -> new UsbLightManager(this)); 329 } 330 331 /** 332 * get the action model resource bundle 333 * 334 * @return the ResourceBundle 335 */ 336 @Override 337 protected ResourceBundle getActionModelResourceBundle() { 338 log.debug("* getActionModelResourceBundle()"); 339 return null; 340 } 341 342 @Override 343 public <B extends NamedBean> Comparator<B> getNamedBeanComparator(Class<B> type) { 344 return new NamedBeanComparator<>(); 345 } 346 347 /** 348 * dispose 349 */ 350 @Override 351 public void dispose() { 352 log.debug("* dispose()"); 353 InstanceManager.deregister(this, AnymaDMX_SystemConnectionMemo.class); 354 super.dispose(); 355 } 356 357 private final static Logger log 358 = LoggerFactory.getLogger(AnymaDMX_SystemConnectionMemo.class); 359}