001package jmri.jmrix.dccpp; 002 003import static jmri.jmrix.dccpp.DCCppConstants.MAX_SENSOR_ID; 004 005import java.util.Locale; 006import javax.annotation.Nonnull; 007import jmri.JmriException; 008import jmri.Sensor; 009import org.slf4j.Logger; 010import org.slf4j.LoggerFactory; 011 012/** 013 * Implement SensorManager for DCC++ systems. 014 * <p> 015 * System names are "DSnnn", where D is the user configurable system prefix, 016 * nnn is the sensor number without padding. 017 * 018 * @author Paul Bender Copyright (C) 2003-2010 019 * @author Mark Underwood Copyright (C) 2015 020 */ 021public class DCCppSensorManager extends jmri.managers.AbstractSensorManager implements DCCppListener { 022 023 protected DCCppTrafficController tc = null; 024 025 /** 026 * Create an new DCC++ SensorManager. 027 * Has to register for DCC++ events. 028 * 029 * @param memo the supporting system connection memo 030 */ 031 public DCCppSensorManager(DCCppSystemConnectionMemo memo) { 032 super(memo); 033 tc = memo.getDCCppTrafficController(); 034 // set up listener 035 tc.addDCCppListener(DCCppInterface.FEEDBACK, this); 036 // request list of sensors 037 DCCppMessage msg = DCCppMessage.makeSensorListMsg(); 038 tc.sendDCCppMessage(msg, this); 039 } 040 041 /** 042 * {@inheritDoc} 043 */ 044 @Override 045 @Nonnull 046 public DCCppSystemConnectionMemo getMemo() { 047 return (DCCppSystemConnectionMemo) memo; 048 } 049 050 // to free resources when no longer used 051 @Override 052 public void dispose() { 053 tc.removeDCCppListener(DCCppInterface.FEEDBACK, this); 054 super.dispose(); 055 } 056 057 // DCCpp specific methods 058 059 /** 060 * {@inheritDoc} 061 * 062 * @throws IllegalArgumentException when SystemName can't be converted 063 */ 064 @Override 065 @Nonnull 066 protected Sensor createNewSensor(@Nonnull String systemName, String userName) throws IllegalArgumentException { 067 int addr; 068 try { 069 addr = Integer.parseInt(systemName.substring(getSystemPrefix().length() + 1)); 070 } catch (NumberFormatException e) { 071 throw new IllegalArgumentException("Can't convert " + // NOI18N 072 systemName.substring(getSystemNamePrefix().length()) + 073 " to DCC++ sensor address"); // NOI18N 074 } 075 return new DCCppSensor(getSystemNamePrefix() + addr, userName, tc); 076 } 077 078 /** 079 * Listen for sensors, creating them as needed. 080 * 081 * @param l the message to parse 082 */ 083 @Override 084 public void message(DCCppReply l) { 085 int addr = -1; // -1 flags that no sensor address was found in reply 086 if (l.isSensorDefReply()) { 087 addr = l.getSensorDefNumInt(); 088 if (log.isDebugEnabled()) { 089 log.debug("SensorDef Reply for Encoder {}", Integer.toString(addr)); 090 } 091 092 } else if (l.isSensorReply()) { 093 addr = l.getSensorNumInt(); 094 if (log.isDebugEnabled()) { 095 log.debug("Sensor Status Reply for Encoder {}", Integer.toString(addr)); 096 } 097 } 098 if (addr >= 0) { 099 String s = getSystemNamePrefix() + (addr); 100 if (null == getBySystemName(s)) { 101 // The sensor doesn't exist. We need to create a 102 // new sensor, and forward this message to it. 103 DCCppSensor sn = (DCCppSensor) provideSensor(s); 104 sn.initmessage(l); 105 } else { 106 // The sensor exists. We need to forward this 107 // message to it. 108 Sensor sen = getBySystemName(s); 109 if (sen == null) { 110 log.error("Failed to get sensor for {}", s); 111 } else { 112 ((DCCppSensor) sen).message(l); 113 } 114 } 115 } 116 } 117 118 /** 119 * Listen for the outgoing messages (to the command station) 120 * 121 * @param l the message to parse 122 */ 123 @Override 124 public void message(DCCppMessage l) { 125 } 126 127 // Handle message timeout notification 128 // If the message still has retries available, reduce retries and send it back to the traffic controller. 129 @Override 130 public void notifyTimeout(DCCppMessage msg) { 131 log.debug("Notified of timeout on message '{}' , {} retries available.", msg, msg.getRetries()); 132 if (msg.getRetries() > 0) { 133 msg.setRetries(msg.getRetries() - 1); 134 tc.sendDCCppMessage(msg, this); 135 } 136 } 137 138 @Override 139 public boolean allowMultipleAdditions(@Nonnull String systemName) { 140 return true; 141 } 142 143 @Override 144 @Nonnull 145 synchronized public String createSystemName(@Nonnull String curAddress, @Nonnull String prefix) throws JmriException { 146 int encoderAddress = 0; 147 int input = 0; 148 149 if (curAddress.contains(":")) { 150 // Address format passed is in the form of encoderAddress:input or T:turnout address 151 int seperator = curAddress.indexOf(":"); 152 try { 153 encoderAddress = Integer.parseInt(curAddress.substring(0, seperator)); 154 input = Integer.parseInt(curAddress.substring(seperator + 1)); 155 } catch (NumberFormatException ex) { 156 throw new JmriException("Unable to convert " + curAddress + " into the cab and input format of nn:xx"); 157 } 158 iName = ((encoderAddress - 1) * 8) + input; 159 } else { 160 // Entered in using the old format 161 try { 162 iName = Integer.parseInt(curAddress); 163 } catch (NumberFormatException ex) { 164 throw new JmriException("Hardware Address "+curAddress+" should be a number or cab and input format of nn:xx"); 165 } 166 } 167 168 return prefix + typeLetter() + iName; 169 } 170 171 int iName; // must synchronize to avoid race conditions. 172 173 /** 174 * {@inheritDoc} 175 */ 176 @Override 177 public NameValidity validSystemNameFormat(@Nonnull String systemName) { 178 return (getBitFromSystemName(systemName) != 0) ? NameValidity.VALID : NameValidity.INVALID; 179 } 180 181 /** 182 * {@inheritDoc} 183 */ 184 @Override 185 public String validateSystemNameFormat(String systemName, Locale locale) { 186 return validateIntegerSystemNameFormat(systemName, 1, MAX_SENSOR_ID, locale); 187 } 188 189 /** 190 * Get the bit address from the system name. 191 * @param systemName a valid Sensor System Name 192 * @return the sensor number extracted from the system name 193 */ 194 public int getBitFromSystemName(String systemName) { 195 try { 196 validateSystemNameFormat(systemName, Locale.getDefault()); 197 } catch (IllegalArgumentException ex) { 198 return 0; 199 } 200 return Integer.parseInt(systemName.substring(getSystemNamePrefix().length())); 201 } 202 203 /** 204 * {@inheritDoc} 205 */ 206 @Override 207 public String getEntryToolTip() { 208 return Bundle.getMessage("AddOutputEntryToolTip"); 209 } 210 211 private final static Logger log = LoggerFactory.getLogger(DCCppSensorManager.class); 212 213}