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