001package jmri.jmrix.bidib; 002 003import java.util.ArrayList; 004import java.util.Locale; 005import javax.annotation.Nonnull; 006import jmri.JmriException; 007import jmri.Light; 008import jmri.managers.AbstractLightManager; 009import org.slf4j.Logger; 010import org.slf4j.LoggerFactory; 011 012/** 013 * Implement LightManager for BiDiB systems. 014 * 015 * @author Paul Bender Copyright (C) 2008 016 * @author Eckart Meyer Copyright (C) 2019 017 */ 018public class BiDiBLightManager extends AbstractLightManager { 019 020 private BiDiBTrafficController tc = null; 021 022 // Whether we accumulate partially loaded turnouts in pendingLights. 023 private boolean isLoading = false; 024 // Lights that are being loaded from XML. 025 private final ArrayList<BiDiBLight> pendingLights = new ArrayList<>(); 026 027 028 public BiDiBLightManager(BiDiBSystemConnectionMemo memo) { 029 super(memo); 030 this.tc = memo.getBiDiBTrafficController(); 031 } 032 033 /** 034 * {@inheritDoc} 035 */ 036 @Override 037 public BiDiBSystemConnectionMemo getMemo() { 038 return (BiDiBSystemConnectionMemo) memo; 039 } 040 041 /** 042 * Create a new Light based on the system name. 043 * Assumes calling method has checked that a Light with this 044 * system name does not already exist. 045 * 046 * @return null if the system name is not in a valid format. 047 */ 048 @Override 049 public Light createNewLight(String systemName, String userName) { 050 log.trace("createNewLight {} - {}", systemName, userName); 051 052 // first, check validity 053 try { 054 validateSystemNameFormat(systemName); 055 } catch (IllegalArgumentException e) { 056 log.error("Error validating", e); 057 throw e; 058 } 059 // OK, make 060 BiDiBLight lgt = new BiDiBLight(systemName, this); 061 lgt.setUserName(userName); 062 063 synchronized (pendingLights) { 064 if (isLoading) { 065 pendingLights.add(lgt); 066 } else { 067 lgt.finishLoad(); 068 } 069 } 070 071 return lgt; 072 } 073 074 /** 075 * This function is invoked before an XML load is started. We defer initialization of the 076 * newly created turnouts until finishLoad because the feedback type might be changing as we 077 * are parsing the XML. 078 */ 079 public void startLoad() { 080 log.debug("Light manager : start load"); 081 synchronized (pendingLights) { 082 isLoading = true; 083 } 084 } 085 086 /** 087 * This function is invoked after the XML load is complete and all Sensors are instantiated 088 * and their type is read in. We use this hook to finalize the construction of the 089 * objects whose instantiation was deferred until the feedback type was known. 090 */ 091 public void finishLoad() { 092 log.info("Light manager : finish load"); 093 synchronized (pendingLights) { 094 pendingLights.forEach((s) -> { 095 s.finishLoad(); 096 }); 097 pendingLights.clear(); 098 isLoading = false; 099 } 100 } 101 102 /** 103 * {@inheritDoc} 104 */ 105 @Override 106 public String createSystemName(@Nonnull String curAddress, @Nonnull String prefix) throws JmriException { //TODO some validation? Throw exception then? - see parent class 107 log.trace("createSystemName from {} - {}", curAddress, prefix); 108 try { 109 int i = 1; 110 int curNum = Integer.parseInt(curAddress); 111 for (Light lgt : getNamedBeanSet()) { 112 //log.trace("turnout: {}/{} {}", i, curNum, lgt.getSystemName()); 113 if (i++ == curNum) { 114 return lgt.getSystemName(); 115 } 116 } 117 } catch (java.lang.NumberFormatException ex) { 118 throw new JmriException("Hardware Address passed "+curAddress+" should be a number"); 119 } 120 return prefix + typeLetter() + curAddress; 121 } 122 123 /** 124 * {@inheritDoc} 125 */ 126 @Override 127 public String validateSystemNameFormat(String name, Locale locale) { 128 log.trace("validateSystemNameFormat: name: {}, typeLetter: {}", name, typeLetter()); 129 validateSystemNamePrefix(name, locale); 130 //validateAddressFormat(name.substring(getSystemNamePrefix().length())); 131 if (!BiDiBAddress.isValidSystemNameFormat(name, typeLetter(), getMemo())) { 132 throw new jmri.NamedBean.BadSystemNameException(Locale.getDefault(), "InvalidSystemName",name); 133 } 134 return name; 135 } 136 137 /** 138 * {@inheritDoc} 139 */ 140 @Override 141 public NameValidity validSystemNameFormat(String systemName) { 142 if (systemName.length() <= getSystemPrefix().length() ) { 143 return NameValidity.INVALID; 144 } 145// try { 146// validateAddressFormat(addr); 147// } catch (IllegalArgumentException e) { 148// return NameValidity.INVALID; 149// } 150 return NameValidity.VALID; 151 } 152 153 /** 154 * Validate system name for configuration. 155 * 156 * @param systemName system name to validate 157 * @return 'true' if system name has a valid meaning in current configuration, else returns 158 * 'false'. For now, this method always returns 'true'; it is needed for the 159 * Abstract Light class. 160 */ 161 @Override 162 public boolean validSystemNameConfig(String systemName) { 163 return (true); 164 } 165 166 /** 167 * Determine if it is possible to add a range of lights in 168 * numerical order eg 11 thru 18, primarily used to enable/disable the Add 169 * range checkbox in the Add Light pane. 170 * 171 * @param systemName system name to check for (not used so far) 172 * @return true if multiple additions are possible. For now, this is always the case. 173 */ 174 @Override 175 public boolean allowMultipleAdditions(String systemName) { 176 return true; 177 } 178 179 /** 180 * {@inheritDoc} 181 */ 182 @Override 183 public String getEntryToolTip() { 184 return Bundle.getMessage("AddOutputEntryToolTip"); 185 } 186 187 /** 188 * Request config from all LC ports. The resulting config messages are processed by the Message Listeners of the Light and Sensor instances. 189 */ 190 public void configAll() { 191 log.trace("configAll tc: {}", tc); 192 log.debug("LightManager config must be called after SensorManager config. If this changes in JMRI, sensor ports won't be checked!"); 193 tc.allPortConfigX(); 194 } 195 196 private final static Logger log = LoggerFactory.getLogger(BiDiBLightManager.class); 197 198}