001package jmri.jmrix.openlcb; 002 003import java.util.*; 004 005import javax.annotation.Nonnull; 006import jmri.BooleanPropertyDescriptor; 007import jmri.JmriException; 008import jmri.NamedBean; 009import jmri.NamedBeanPropertyDescriptor; 010import jmri.Sensor; 011import jmri.jmrix.can.CanListener; 012import jmri.jmrix.can.CanMessage; 013import jmri.jmrix.can.CanReply; 014import jmri.jmrix.can.CanSystemConnectionMemo; 015 016/** 017 * Manage the OpenLCB-specific Sensor implementation. 018 * 019 * System names are "MSnnn", where M is the user configurable system prefix, 020 * nnn is the sensor number without padding. 021 * 022 * @author Bob Jacobsen Copyright (C) 2008, 2010 023 */ 024public class OlcbSensorManager extends jmri.managers.AbstractSensorManager implements CanListener { 025 026 // Whether we accumulate partially loaded objects in pendingSensors. 027 private boolean isLoading = false; 028 // Turnouts that are being loaded from XML. 029 private final ArrayList<OlcbSensor> pendingSensors = new ArrayList<>(); 030 031 /** 032 * {@inheritDoc} 033 */ 034 @Override 035 @Nonnull 036 public CanSystemConnectionMemo getMemo() { 037 return (CanSystemConnectionMemo) memo; 038 } 039 040 @Override 041 @Nonnull 042 public List<NamedBeanPropertyDescriptor<?>> getKnownBeanProperties() { 043 List<NamedBeanPropertyDescriptor<?>> l = new ArrayList<>(); 044 l.add(new BooleanPropertyDescriptor(OlcbUtils.PROPERTY_IS_AUTHORITATIVE, OlcbTurnout 045 .DEFAULT_IS_AUTHORITATIVE) { 046 @Override 047 public String getColumnHeaderText() { 048 return Bundle.getMessage("OlcbStateAuthHeader"); 049 } 050 051 @Override 052 public boolean isEditable(NamedBean bean) { 053 return OlcbUtils.isOlcbBean(bean); 054 } 055 }); 056 l.add(new BooleanPropertyDescriptor(OlcbUtils.PROPERTY_LISTEN, OlcbTurnout 057 .DEFAULT_LISTEN) { 058 @Override 059 public String getColumnHeaderText() { 060 return Bundle.getMessage("OlcbStateListenHeader"); 061 } 062 063 @Override 064 public boolean isEditable(NamedBean bean) { 065 return OlcbUtils.isOlcbBean(bean); 066 } 067 }); 068 return l; 069 } 070 071 // to free resources when no longer used 072 @Override 073 public void dispose() { 074 getMemo().getTrafficController().removeCanListener(this); 075 super.dispose(); 076 } 077 078 // Implemented ready for new system connection memo 079 public OlcbSensorManager(CanSystemConnectionMemo memo) { 080 super(memo); 081 memo.getTrafficController().addCanListener(this); 082 } 083 084 /** 085 * {@inheritDoc} 086 * 087 * @throws IllegalArgumentException when SystemName can't be converted 088 */ 089 @Override 090 @Nonnull 091 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( value = "SLF4J_FORMAT_SHOULD_BE_CONST", 092 justification = "passing exception text") 093 protected Sensor createNewSensor(@Nonnull String systemName, String userName) throws IllegalArgumentException { 094 String addr = systemName.substring(getSystemNamePrefix().length()); 095 // first, check validity 096 try { 097 validateSystemNameFormat(systemName,Locale.getDefault()); 098 } catch (jmri.NamedBean.BadSystemNameException e) { 099 log.error(e.getMessage()); 100 throw e; 101 } 102 // OK, make 103 OlcbSensor s = new OlcbSensor(getSystemPrefix(), addr, (CanSystemConnectionMemo) memo); 104 s.setUserName(userName); 105 106 synchronized (pendingSensors) { 107 if (isLoading) { 108 pendingSensors.add(s); 109 } else { 110 s.finishLoad(); 111 } 112 } 113 return s; 114 } 115 116 /** 117 * This function is invoked before an XML load is started. We defer initialization of the 118 * newly created Sensors until finishLoad because the feedback type might be changing as we 119 * are parsing the XML. 120 */ 121 public void startLoad() { 122 log.debug("Sensor manager : start load"); 123 synchronized (pendingSensors) { 124 isLoading = true; 125 } 126 } 127 128 /** 129 * This function is invoked after the XML load is complete and all Sensors are instantiated 130 * and their feedback type is read in. We use this hook to finalize the construction of the 131 * OpenLCB objects whose instantiation was deferred until the feedback type was known. 132 */ 133 public void finishLoad() { 134 log.debug("Sensor manager : finish load"); 135 synchronized (pendingSensors) { 136 pendingSensors.forEach(OlcbSensor::finishLoad); 137 pendingSensors.clear(); 138 isLoading = false; 139 } 140 } 141 142 @Override 143 public boolean allowMultipleAdditions(@Nonnull String systemName) { 144 return false; 145 } 146 147 @Override 148 @Nonnull 149 public String createSystemName(@Nonnull String curAddress, @Nonnull String prefix) throws JmriException { 150 String tmpPrefix = prefix + typeLetter(); 151 String tmpSName = tmpPrefix + curAddress; 152 try { 153 OlcbAddress.validateSystemNameFormat(tmpSName,Locale.getDefault(),tmpPrefix, (CanSystemConnectionMemo) memo); 154 } 155 catch ( jmri.NamedBean.BadSystemNameException ex ){ 156 throw new JmriException(ex.getMessage()); 157 } 158 // don't check for integer; should check for validity here 159 return prefix + typeLetter() + curAddress; 160 } 161 162 @Override 163 @javax.annotation.Nonnull 164 @javax.annotation.CheckReturnValue 165 public String getNextValidSystemName(@Nonnull NamedBean currentBean) throws JmriException { 166 throw new jmri.JmriException("getNextValidSystemName should not have been called"); 167 } 168 169 /** 170 * {@inheritDoc} 171 */ 172 @Override 173 public String getEntryToolTip() { 174 return Bundle.getMessage("AddSensorEntryToolTip"); 175 } 176 177 // listen for sensors, creating them as needed 178 @Override 179 public void reply(CanReply l) { 180 // doesn't do anything, because for now 181 // we want you to create manually 182 } 183 184 @Override 185 public void message(CanMessage l) { 186 // doesn't do anything, because 187 // messages come from us 188 } 189 190 /** 191 * Validates to OpenLCB format. 192 * {@inheritDoc} 193 */ 194 @Override 195 @Nonnull 196 public String validateSystemNameFormat(@Nonnull String name, @Nonnull java.util.Locale locale) throws jmri.NamedBean.BadSystemNameException { 197 name = super.validateSystemNameFormat(name,locale); 198 name = OlcbAddress.validateSystemNameFormat(name,locale,getSystemNamePrefix(), (CanSystemConnectionMemo) memo); 199 return name; 200 } 201 202 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(OlcbSensorManager.class); 203 204} 205 206