001package jmri.jmrix.cmri.serial; 002 003import java.util.Locale; 004import javax.annotation.Nonnull; 005import jmri.JmriException; 006import jmri.Sensor; 007import jmri.jmrix.AbstractNode; 008import jmri.jmrix.cmri.CMRISystemConnectionMemo; 009import org.slf4j.Logger; 010import org.slf4j.LoggerFactory; 011 012/** 013 * Manage the C/MRI serial-specific Sensor implementation. 014 * <p> 015 * System names are "CSnnnn", where C is the user-configurable system prefix, 016 * nnnn is the sensor number without padding. 017 * <p> 018 * Sensors are numbered from 1. 019 * <p> 020 * This is a SerialListener to handle the replies to poll messages. Those are 021 * forwarded to the specific SerialNode object corresponding to their origin for 022 * processing of the data. 023 * 024 * @author Bob Jacobsen Copyright (C) 2003, 2007 025 * @author Dave Duchamp, multi node extensions, 2004 026 */ 027public class SerialSensorManager extends jmri.managers.AbstractSensorManager 028 implements SerialListener { 029 030 /** 031 * Number of sensors per UA in the naming scheme. 032 * <p> 033 * The first UA (node address) uses sensors from 1 to SENSORSPERUA-1, the 034 * second from SENSORSPERUA+1 to SENSORSPERUA+(SENSORSPERUA-1), etc. 035 * <p> 036 * Must be more than, and is generally one more than, 037 * {@link SerialNode#MAXSENSORS} 038 * 039 */ 040 static final int SENSORSPERUA = 1000; 041 042 public SerialSensorManager(CMRISystemConnectionMemo memo) { 043 super(memo); 044 } 045 046 /** 047 * {@inheritDoc} 048 */ 049 @Override 050 @Nonnull 051 public CMRISystemConnectionMemo getMemo() { 052 return (CMRISystemConnectionMemo) memo; 053 } 054 055 /** 056 * {@inheritDoc} 057 * <p> 058 * System name is normalized to ensure uniqueness. 059 * 060 * @throws IllegalArgumentException when SystemName can't be converted 061 */ 062 @Override 063 @Nonnull 064 protected Sensor createNewSensor(@Nonnull String systemName, String userName) throws IllegalArgumentException { 065 Sensor s; 066 // validate the system name, and normalize it 067 String sName = getMemo().normalizeSystemName(systemName); 068 if (sName.isEmpty()) { 069 // system name is not valid 070 throw new IllegalArgumentException("Invalid C/MRI Sensor system name - " + // NOI18N 071 systemName); 072 } 073 // does this Sensor already exist 074 s = getBySystemName(sName); 075 if (s != null) { 076 throw new IllegalArgumentException("C/MRI Sensor with this name already exists - " + // NOI18N 077 systemName); 078 } 079 // check under alternate name 080 String altName = getMemo().convertSystemNameToAlternate(sName); 081 s = getBySystemName(altName); 082 if (s != null) { 083 throw new IllegalArgumentException("C/MRI Sensor with name " + // NOI18N 084 systemName + " already exists as " + altName); 085 } 086 // check bit number 087 int bit = getMemo().getBitFromSystemName(sName); 088 if ((bit <= 0) || (bit >= SENSORSPERUA)) { 089 throw new IllegalArgumentException("Sensor bit number " + // NOI18N 090 Integer.toString(bit) + " is outside the supported range 1-" + 091 Integer.toString(SENSORSPERUA - 1)); 092 } 093 // Sensor system name is valid and Sensor doesn't exist, make a new one 094 if (userName == null) { 095 s = new SerialSensor(sName); 096 } else { 097 s = new SerialSensor(sName, userName); 098 } 099 100 // ensure that a corresponding Serial Node exists 101 SerialNode node = (SerialNode) getMemo().getNodeFromSystemName(sName,getMemo().getTrafficController()); 102 if (node == null) { 103 log.warn("Sensor {} refers to an undefined Serial Node.", sName); 104 return s; 105 } 106 // register this sensor with the Serial Node 107 node.registerSensor(s, bit - 1); 108 return s; 109 } 110 111 /** 112 * Dummy routine 113 */ 114 @Override 115 public void message(SerialMessage r) { 116 log.warn("unexpected message"); 117 } 118 119 /** 120 * Process a reply to a poll of Sensors of one node 121 */ 122 @Override 123 public void reply(SerialReply r) { 124 // determine which node 125 SerialNode node = (SerialNode) getMemo().getTrafficController().getNodeFromAddress(r.getUA()); 126 if (node != null) { 127 node.markChanges(r); 128 } 129 } 130 131 /** 132 * Method to register any orphan Sensors when a new Serial Node is created. 133 * @param node the node with potential orphan sensors. 134 */ 135 public void registerSensorsForNode(SerialNode node) { 136 // get list containing all Sensors 137 AbstractNode tNode; 138 for (Sensor s : getNamedBeanSet()) { 139 String sName = s.getSystemName(); 140 log.debug("system name is {}", sName); 141 if ( sName.startsWith(getSystemNamePrefix()) ){ 142 // This is a C/MRI Sensor 143 tNode = getMemo().getNodeFromSystemName(sName, getMemo().getTrafficController()); 144 if (tNode == node) { 145 // This sensor is for this new Serial Node - register it 146 node.registerSensor(s, 147 (getMemo().getBitFromSystemName(sName) - 1)); 148 } 149 } 150 } 151 } 152 153 /** 154 * {@inheritDoc} 155 */ 156 @Override 157 public boolean allowMultipleAdditions(@Nonnull String systemName) { 158 return true; 159 } 160 161 /** 162 * {@inheritDoc} 163 */ 164 @Override 165 @Nonnull 166 public String createSystemName(@Nonnull String curAddress, @Nonnull String prefix) throws JmriException { 167 String tmpSName = ""; 168 if (curAddress.contains(":")) { 169 //Address format passed is in the form node:address 170 int seperator = curAddress.indexOf(":"); 171 try { 172 nAddress = Integer.parseInt(curAddress.substring(0, seperator)); 173 bitNum = Integer.parseInt(curAddress.substring(seperator + 1)); 174 } catch (NumberFormatException ex) { 175 throw new JmriException("Unable to convert " + curAddress + " to a number."); 176 } 177 tmpSName = getMemo().makeSystemName("S", nAddress, bitNum); 178 } else if (curAddress.contains("B") || (curAddress.contains("b"))) { 179 curAddress = curAddress.toUpperCase(); 180 try { 181 //We do this to simply check that we have numbers in the correct places ish 182 Integer.parseInt(curAddress.substring(0, 1)); 183 int b = (curAddress.toUpperCase()).indexOf("B") + 1; 184 Integer.parseInt(curAddress.substring(b)); 185 } catch (NumberFormatException ex) { 186 throw new JmriException("Unable to convert " + curAddress + " to a number"); 187 } 188 tmpSName = prefix + typeLetter() + curAddress; 189 bitNum = getMemo().getBitFromSystemName(tmpSName); 190 nAddress = getMemo().getNodeAddressFromSystemName(tmpSName); 191 } else { 192 try { 193 //We do this to simply check that the value passed is a number! 194 Integer.parseInt(curAddress); 195 } catch (NumberFormatException ex) { 196 throw new JmriException("Unable to convert " + curAddress + " to a valid Hardware Address"); 197 } 198 tmpSName = prefix + typeLetter() + curAddress; 199 bitNum = getMemo().getBitFromSystemName(tmpSName); 200 nAddress = getMemo().getNodeAddressFromSystemName(tmpSName); 201 } 202 203 return tmpSName; 204 } 205 206 private int bitNum = 0; 207 private int nAddress = 0; 208 209 /** 210 * {@inheritDoc} 211 */ 212 @Override 213 @Nonnull 214 public String validateSystemNameFormat(@Nonnull String systemName, @Nonnull Locale locale) { 215 return getMemo().validateSystemNameFormat(super.validateSystemNameFormat(systemName, locale), typeLetter(), locale); 216 } 217 218 /** 219 * {@inheritDoc} 220 */ 221 @Override 222 public NameValidity validSystemNameFormat(@Nonnull String systemName) { 223 return getMemo().validSystemNameFormat(systemName, typeLetter()); 224 } 225 226 /** 227 * {@inheritDoc} 228 */ 229 @Override 230 public String getEntryToolTip() { 231 return Bundle.getMessage("AddInputEntryToolTip"); 232 } 233 234 private final static Logger log = LoggerFactory.getLogger(SerialSensorManager.class); 235 236}