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