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 protected Sensor createNewSensor(@Nonnull String systemName, String userName) throws IllegalArgumentException { 095 String addr = systemName.substring(getSystemNamePrefix().length()); 096 // first, check validity 097 try { 098 validateSystemNameFormat(systemName,Locale.getDefault()); 099 } catch (jmri.NamedBean.BadSystemNameException e) { 100 log.error(e.getMessage()); 101 throw e; 102 } 103 // OK, make 104 OlcbSensor s = new OlcbSensor(getSystemPrefix(), addr, memo.get(OlcbInterface.class)); 105 s.setUserName(userName); 106 107 synchronized (pendingSensors) { 108 if (isLoading) { 109 pendingSensors.add(s); 110 } else { 111 s.finishLoad(); 112 } 113 } 114 return s; 115 } 116 117 /** 118 * This function is invoked before an XML load is started. We defer initialization of the 119 * newly created Sensors until finishLoad because the feedback type might be changing as we 120 * are parsing the XML. 121 */ 122 public void startLoad() { 123 log.debug("Sensor manager : start load"); 124 synchronized (pendingSensors) { 125 isLoading = true; 126 } 127 } 128 129 /** 130 * This function is invoked after the XML load is complete and all Sensors are instantiated 131 * and their feedback type is read in. We use this hook to finalize the construction of the 132 * OpenLCB objects whose instantiation was deferred until the feedback type was known. 133 */ 134 public void finishLoad() { 135 log.debug("Sensor manager : finish load"); 136 synchronized (pendingSensors) { 137 pendingSensors.forEach(OlcbSensor::finishLoad); 138 pendingSensors.clear(); 139 isLoading = false; 140 } 141 } 142 143 @Override 144 public boolean allowMultipleAdditions(@Nonnull String systemName) { 145 return false; 146 } 147 148 @Override 149 @Nonnull 150 public String createSystemName(@Nonnull String curAddress, @Nonnull String prefix) throws JmriException { 151 try { 152 OlcbAddress.validateSystemNameFormat(curAddress,Locale.getDefault(),prefix); 153 } 154 catch ( jmri.NamedBean.BadSystemNameException ex ){ 155 throw new JmriException(ex.getMessage()); 156 } 157 // don't check for integer; should check for validity here 158 return prefix + typeLetter() + curAddress; 159 } 160 161 @Override 162 public String getNextValidAddress(@Nonnull String curAddress, @Nonnull String prefix, boolean ignoreInitialExisting) { 163 // always return this (the current) name without change 164 return curAddress; 165 } 166 167 /** 168 * {@inheritDoc} 169 */ 170 @Override 171 public String getEntryToolTip() { 172 return Bundle.getMessage("AddSensorEntryToolTip"); 173 } 174 175 // listen for sensors, creating them as needed 176 @Override 177 public void reply(CanReply l) { 178 // doesn't do anything, because for now 179 // we want you to create manually 180 } 181 182 @Override 183 public void message(CanMessage l) { 184 // doesn't do anything, because 185 // messages come from us 186 } 187 188 /** 189 * No mechanism currently exists to request status updates from all layout 190 * sensors. 191 */ 192 @Override 193 public void updateAll() { 194 // no current mechanisim to request status updates from all layout sensors 195 } 196 197 /** 198 * Validates to OpenLCB format. 199 * {@inheritDoc} 200 */ 201 @Override 202 @Nonnull 203 public String validateSystemNameFormat(@Nonnull String name, @Nonnull java.util.Locale locale) throws jmri.NamedBean.BadSystemNameException { 204 name = super.validateSystemNameFormat(name,locale); 205 name = OlcbAddress.validateSystemNameFormat(name,locale,getSystemNamePrefix()); 206 return name; 207 } 208 209 private static final Logger log = LoggerFactory.getLogger(OlcbSensorManager.class); 210 211} 212 213