001package jmri.jmrix.can; 002 003import java.util.Comparator; 004import java.util.HashMap; 005import java.util.Map; 006import java.util.ResourceBundle; 007import java.util.Set; 008import javax.annotation.Nonnull; 009 010import jmri.*; 011import jmri.jmrix.ConfiguringSystemConnectionMemo; 012import jmri.jmrix.DefaultSystemConnectionMemo; 013import jmri.jmrix.can.ConfigurationManager.SubProtocol; 014import jmri.jmrix.can.ConfigurationManager.ProgModeSwitch; 015import jmri.util.NamedBeanComparator; 016 017import jmri.util.startup.StartupActionFactory; 018import org.slf4j.Logger; 019import org.slf4j.LoggerFactory; 020 021/** 022 * Lightweight class to denote that a system is active, and provide general 023 * information. 024 * <p> 025 * As various CAN adapters, can work with different CAN Bus systems, the adapter 026 * memo is generic for all adapters, it then uses a ConfigurationManager for 027 * each of the CAN Bus systems. Any requests for provision or configuration is 028 * passed on to the relevant ConfigurationManager to handle. 029 * 030 * @author Kevin Dickerson Copyright (C) 2012 031 * @author Andrew Crosland Copyright (C) 2021 032 */ 033public class CanSystemConnectionMemo extends DefaultSystemConnectionMemo implements ConfiguringSystemConnectionMemo { 034 // This user name will be overwritten by the adapter and saved to the connection config. 035 public static final String DEFAULT_USERNAME = "CAN"; 036 037 private boolean protocolOptionsChanged = false; 038 039 /** 040 * Create a new CanSystemConnectionMemo. 041 * Default prefix: M 042 * Default Username: CAN 043 */ 044 public CanSystemConnectionMemo() { 045 super("M", DEFAULT_USERNAME); 046 } 047 048 /** 049 * Create a new CanSystemConnectionMemo. 050 * Allows for default systemPrefix other than "M" 051 * Default Username: CAN 052 * @param prefix System prefix to use, e.g. M. 053 */ 054 public CanSystemConnectionMemo(String prefix) { 055 super(prefix, DEFAULT_USERNAME); 056 } 057 058 protected final void storeCanMemotoInstance() { 059 register(); // registers general type 060 InstanceManager.store(this, CanSystemConnectionMemo.class); // also register as specific type 061 } 062 063 protected String _protocol = ConfigurationManager.MERGCBUS; 064 protected SubProtocol _subProtocol = SubProtocol.CBUS; 065 protected ProgModeSwitch _progModeSwitch = ProgModeSwitch.NONE; 066 protected boolean _supportsCVHints = false; // Support for CV read hint values 067 private boolean _multipleThrottles = true; // Support for multiple throttles 068 private boolean _powerOnArst = true; // Turn power on if ARST opcode received 069 070 jmri.jmrix.swing.ComponentFactory cf = null; 071 072 protected TrafficController tm; 073 074 /** 075 * Set Connection Traffic Controller. 076 * @param tm System Connection Traffic Controller 077 */ 078 public void setTrafficController(TrafficController tm) { 079 this.tm = tm; 080 } 081 082 /** 083 * Get Connection Traffic Controller. 084 * @return System Connection Traffic Controller 085 */ 086 public TrafficController getTrafficController() { 087 return tm; 088 } 089 090 private jmri.jmrix.can.ConfigurationManager manager; 091 092 private final Map<String, Map<String, String>> protocolOptions = new HashMap<>(); 093 094 /** 095 * {@inheritDoc } 096 * Searches ConfigurationManager for class before super. 097 */ 098 @Override 099 public boolean provides(Class<?> type) { 100 if (getDisabled()) { 101 return false; 102 } 103 if (manager == null) { 104 return false; 105 } 106 if (type.equals(jmri.GlobalProgrammerManager.class)) { 107 jmri.GlobalProgrammerManager mgr = get(jmri.GlobalProgrammerManager.class); 108 if (mgr == null) return false; 109 return mgr.isGlobalProgrammerAvailable(); 110 } 111 if (type.equals(jmri.AddressedProgrammerManager.class)) { 112 jmri.AddressedProgrammerManager mgr = get(jmri.AddressedProgrammerManager.class); 113 if (mgr == null) return false; 114 return mgr.isAddressedModePossible(); 115 } 116 117 boolean result = manager.provides(type); 118 if(result) { 119 return true; 120 } else { 121 return super.provides(type); 122 } 123 } 124 125 /** 126 * {@inheritDoc } 127 * Searches ConfigurationManager for class before super. 128 */ 129 @SuppressWarnings("unchecked") 130 @Override 131 public <T> T get(Class<T> T) { 132 if (manager != null && !getDisabled()) { 133 T existing = manager.get(T); 134 if ( existing !=null ) { 135 return existing; 136 } 137 } 138 return super.get(T); 139 } 140 141 /** 142 * Get a class directly from the memo Class Object Map. 143 * @param <T> Class type 144 * @param T Class type 145 * @return object in class object map, or null if not present. 146 */ 147 @SuppressWarnings("unchecked") 148 public <T> T getFromMap(Class<?> T) { 149 return (T) classObjectMap.get(T); 150 } 151 152 /** 153 * Get the Protocol in use by the CAN Connection. 154 * e.g. ConfigurationManager.SPROGCBUS 155 * @return ConfigurationManager constant of protocol. 156 */ 157 public String getProtocol() { 158 return _protocol; 159 } 160 161 /** 162 * Set the protocol in use by the connection. 163 * @param protocol e.g. ConfigurationManager.SPROGCBUS 164 */ 165 public void setProtocol(String protocol) { 166 StartupActionFactory old = getActionFactory(); 167 if (null != protocol) { 168 _protocol = protocol; 169 switch (protocol) { 170 case ConfigurationManager.SPROGCBUS: 171 case ConfigurationManager.MERGCBUS: 172 manager = new jmri.jmrix.can.cbus.CbusConfigurationManager(this); 173 break; 174 case ConfigurationManager.OPENLCB: 175 manager = new jmri.jmrix.openlcb.OlcbConfigurationManager(this); 176 break; 177 case ConfigurationManager.RAWCAN: 178 manager = new jmri.jmrix.can.CanConfigurationManager(this); 179 break; 180 case ConfigurationManager.TEST: 181 manager = new jmri.jmrix.can.nmranet.NmraConfigurationManager(this); 182 break; 183 default: 184 break; 185 } 186 } 187 firePropertyChange("actionFactory", old, getActionFactory()); 188 } 189 190 /** 191 * Get ENUM of the sub protocol. 192 * @return the sub protocol in use, e.g. SubProtocol.CBUS 193 */ 194 public SubProtocol getSubProtocol() { 195 return _subProtocol; 196 } 197 198 /** 199 * Set the sub protocol ENUM. 200 * @param sp e.g. SubProtocol.CBUS 201 */ 202 public void setSubProtocol(SubProtocol sp) { 203 if (null != sp) { 204 _subProtocol = sp; 205 } 206 } 207 208 /** 209 * Get the state of the programming mode switch which indicates what combination 210 * of service and/or ops mode programming is supported by the connection. 211 * 212 * @return the supported modes 213 */ 214 public ProgModeSwitch getProgModeSwitch() { 215 return _progModeSwitch; 216 } 217 218 public void setProgModeSwitch(ProgModeSwitch pms) { 219 if (null != pms) { 220 _progModeSwitch = pms; 221 } 222 } 223 224 /** 225 * Some connections support only a single throttle, e.g., a service mode programmer 226 * that allows for test running of a single loco. 227 * 228 * @return true if mutltiple throttles are available 229 */ 230 public boolean hasMultipleThrottles() { 231 return _multipleThrottles; 232 } 233 234 public void setMultipleThrottles(boolean b) { 235 _multipleThrottles = b; 236 } 237 238 /** 239 * Get the CV hint support flag 240 * 241 * @return true if CV hints are supported 242 */ 243 public boolean supportsCVHints() { 244 return _supportsCVHints; 245 } 246 247 public void setSupportsCVHints(boolean b) { 248 _supportsCVHints = b; 249 } 250 251 /** 252 * Get the behaviour on ARST opcode 253 * 254 * @return true if track power is on after ARST 255 */ 256 public boolean powerOnArst() { 257 return _powerOnArst; 258 } 259 260 public void setPowerOnArst(boolean b) { 261 _powerOnArst = b; 262 } 263 264 /** 265 * Configure the common managers for Can connections. 266 * {@inheritDoc } 267 * Calls ConfigurationManager.configureManagers 268 * Stores Can Memo to Instance to indicate available. 269 * 270 */ 271 @Override 272 public void configureManagers() { 273 if (manager != null) { 274 manager.configureManagers(); 275 } 276 storeCanMemotoInstance(); 277 } 278 279 /** 280 * {@inheritDoc } 281 */ 282 @Override 283 protected ResourceBundle getActionModelResourceBundle() { 284 if (manager == null) { 285 return null; 286 } 287 return manager.getActionModelResourceBundle(); 288 } 289 290 /** 291 * {@inheritDoc } 292 */ 293 @Override 294 public <B extends NamedBean> Comparator<B> getNamedBeanComparator(Class<B> type) { 295 return new NamedBeanComparator<>(); 296 } 297 298 /** 299 * Enumerate all protocols that have options set. 300 * 301 * @return set of protocol names. 302 */ 303 public Set<String> getProtocolsWithOptions() { 304 return protocolOptions.keySet(); 305 } 306 307 /** 308 * Get all options we have set (saved in the connection XML) for a given protocol type. 309 * 310 * @param protocol String name of the protocol. 311 * @return map of known protocol options to values, or empty map. 312 */ 313 @Nonnull 314 public Map<String, String> getProtocolAllOptions(String protocol) { 315 return protocolOptions.getOrDefault(protocol, new HashMap<>()); 316 } 317 318 /** 319 * Get a single option of a single protocol, or null if not present. 320 * 321 * @param protocol name of the protocol. 322 * @param option name of the option. 323 * @return null if option has never been set; or the option value if set. 324 */ 325 public synchronized String getProtocolOption(String protocol, String option) { 326 if (!protocolOptions.containsKey(protocol)) return null; 327 Map<String, String> m = getProtocolAllOptions(protocol); 328 return m.getOrDefault(option, null); 329 } 330 331 /** 332 * Sets a protocol option. This list will be persisted when the connection gets saved. 333 * 334 * @param protocol name of the protocol 335 * @param option name of the option 336 * @param value option value 337 */ 338 public synchronized void setProtocolOption(String protocol, String option, String value) { 339 log.debug("Setting protocol option {} {} := {}", protocol, option, value); 340 if (value == null) return; 341 Map<String, String> m = protocolOptions.computeIfAbsent(protocol, k -> new HashMap<>()); 342 String oldValue = m.get(option); 343 if (value.equals(oldValue)) return; 344 m.put(option, value); 345 protocolOptionsChanged = true; 346 } 347 348 @Override 349 public boolean isDirty() { 350 return super.isDirty() || protocolOptionsChanged; 351 } 352 353 @Override 354 public boolean isRestartRequired() { 355 return super.isRestartRequired() || protocolOptionsChanged; 356 } 357 358 /** 359 * Custom interval of 100ms. 360 * {@inheritDoc} 361 */ 362 @Override 363 public int getDefaultOutputInterval(){ 364 return 100; 365 } 366 367 /** 368 * {@inheritDoc } 369 */ 370 @Override 371 public void dispose() { 372 super.dispose(); // remove class map object items before manager config 373 if (manager != null) { 374 manager.dispose(); 375 } 376 tm = null; 377 } 378 379 private static final Logger log = LoggerFactory.getLogger(CanSystemConnectionMemo.class); 380 381}