001package jmri.jmrix.can.cbus.simulator; 002 003import java.util.*; 004 005import javax.annotation.CheckForNull; 006import javax.annotation.Nonnull; 007 008import jmri.jmrix.can.CanSystemConnectionMemo; 009import jmri.jmrix.can.cbus.node.CbusNode; 010import jmri.jmrix.can.cbus.node.CbusNodeConstants; 011 012import jmri.spi.JmriServiceProviderInterface; 013 014import org.slf4j.Logger; 015import org.slf4j.LoggerFactory; 016 017/** 018 * Pane for configuring events in a CBUS module 019 * 020 * Definition of objects to handle configuring a CBUS module. 021 * 022 * Implementing classes <em>must</em> be registered as service providers of this 023 * type to be recognized and usable. 024 * <p> 025 * General design documentation is available on the 026 * <a href="http://jmri.org/help/en/html/doc/Technical/SystemStructure.shtml">Structure of External System Connections page</a>. 027 * 028 * @author Andrew Crosland Copyright (C) 2021 029 * @author Steve Young Copyright (C) 2022 030 * @see java.util.ServiceLoader 031 */ 032public abstract class CbusSimulatedModuleProvider implements JmriServiceProviderInterface { 033 034 /** 035 * Get the Manufacturer ID. 036 * @return manufacturer ID code. 037 */ 038 public abstract int getManufacturerId(); 039 040 /** 041 * Get the Manufacturer Module ID. 042 * @return manufacturer Module ID code. 043 */ 044 public abstract int getModuleId(); 045 046 /** 047 * For a given CbusDummyNode, configure it to the Simulation. 048 * This may include Node Parameters, Node Variables, events and event variables. 049 * @param node the Node to set to. 050 */ 051 public abstract void configureDummyNode(@Nonnull CbusNode node); 052 053 /** 054 * Descriptive String of Module Type. 055 * For use in selection menus etc. 056 * @return descriptive string of simulated module. 057 */ 058 @Nonnull 059 public String getModuleType() { 060 StringBuilder s = new StringBuilder(); 061 s.append(CbusNodeConstants.getManu(getManufacturerId())); 062 s.append(" "); 063 s.append(CbusNodeConstants.getModuleType(getManufacturerId(), getModuleId())); 064 return s.toString(); 065 } 066 067 /** 068 * Descriptive Tooltip for Module Simulation. 069 * For use in selection menus etc. 070 * @return tooltip for the module. 071 */ 072 @Nonnull 073 public String getToolTipText() { 074 return "Simulation of " + CbusNodeConstants.getModuleTypeExtra(getManufacturerId(),getModuleId()); 075 } 076 077 /** 078 * Create a new CbusDummyNode of the implementing class type. 079 * @param nodeNumber Initial Node Number. 080 * @param memo System Connection to use. 081 * @return new Dummy Node of implementing class type. 082 */ 083 @Nonnull 084 public CbusDummyNode getNewDummyNode(CanSystemConnectionMemo memo, int nodeNumber ){ 085 CbusDummyNode nd = createNewDummyNode(memo, nodeNumber); 086 configureDummyNode(nd); 087 CbusNodeConstants.setTraits( nd ); 088 log.info("Simulated CBUS Module: {}", getModuleType() ); 089 return nd; 090 } 091 092 // future classes may want to override this, eg CANMIO-U or CANCMD 093 protected CbusDummyNode createNewDummyNode(CanSystemConnectionMemo memo, int nodeNumber ){ 094 return new CbusDummyNode(memo, nodeNumber); 095 } 096 097 /** 098 * Checks if a Node Manufacturer and Module ID matches this module. 099 * @param nd the Node to test against, can be null. 100 * @return true if they match, else false. 101 */ 102 public boolean matchesManuAndModuleId(@CheckForNull CbusDummyNode nd) { 103 if ( nd == null ) { 104 return false; 105 } 106 return nd.getNodeParamManager().getParameter(1) == getManufacturerId() 107 && nd.getNodeParamManager().getParameter(3) == getModuleId(); 108 } 109 110 /** 111 * Get a module provider from a module name. 112 * 113 * @param name of the module 114 * @return the module provider, null if not known 115 */ 116 @CheckForNull 117 final static public CbusSimulatedModuleProvider getProviderByName(String name) { 118 loadInstances(); 119 return instanceMap.get(name); 120 } 121 122 /** 123 * Get all available instances as an {@link Collections#unmodifiableCollection}. 124 * 125 * @return unmodifiable collection. 126 */ 127 @Nonnull 128 final static public Collection<CbusSimulatedModuleProvider> getInstancesCollection() { 129 loadInstances(); 130 return Collections.unmodifiableCollection(instanceMap.values()); 131 } 132 133 /** 134 * Load all the available instances. Note this only runs 135 * once; there's no reloading once the program is running. 136 */ 137 static private void loadInstances() { 138 if (instanceMap != null) return; 139 140 instanceMap = new TreeMap<>(); // sorted map, in string order on key 141 142 java.util.ServiceLoader.load(CbusSimulatedModuleProvider.class).forEach((module) -> { 143 if (!instanceMap.containsKey(module.getModuleType())) { 144 instanceMap.put(module.getModuleType(), module); 145 } 146 }); 147 148 } 149 150 private static volatile Map<String, CbusSimulatedModuleProvider> instanceMap = null; 151 152 private final static Logger log = LoggerFactory.getLogger(CbusSimulatedModuleProvider.class); 153}