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