001package jmri.jmrix.acela; 002 003import java.util.Locale; 004import javax.annotation.Nonnull; 005import jmri.Sensor; 006import org.slf4j.Logger; 007import org.slf4j.LoggerFactory; 008 009/** 010 * Manage the Acela-specific Sensor implementation. 011 * <p> 012 * System names are "ASnnnn", where A is the user configurable system prefix, 013 * nnnn is the sensor number without padding. 014 * <p> 015 * Sensors are numbered from 0. 016 * <p> 017 * This is an AcelaListener to handle the replies to poll messages. Those are 018 * forwarded to the specific AcelaNode object corresponding to their origin for 019 * processing of the data. 020 * 021 * @author Bob Jacobsen Copyright (C) 2003, 2007 022 * @author Dave Duchamp, multi node extensions, 2004 023 * @author Bob Coleman Copyright (C) 2007, 2008 Based on CMRI serial example, 024 * modified to establish Acela support. 025 */ 026public class AcelaSensorManager extends jmri.managers.AbstractSensorManager 027 implements AcelaListener { 028 029 public AcelaSensorManager(AcelaSystemConnectionMemo memo) { 030 super(memo); 031 } 032 033 /** 034 * {@inheritDoc} 035 */ 036 @Override 037 @Nonnull 038 public AcelaSystemConnectionMemo getMemo() { 039 return (AcelaSystemConnectionMemo) memo; 040 } 041 042 /** 043 * {@inheritDoc} 044 * <p> 045 * System name is normalized to ensure uniqueness. 046 * @throws IllegalArgumentException when SystemName can't be converted 047 */ 048 @Override 049 @Nonnull 050 protected Sensor createNewSensor(@Nonnull String systemName, String userName) throws IllegalArgumentException { 051 // Validate the systemName 052 if (AcelaAddress.validSystemNameFormat(systemName, 'S', getSystemPrefix()) == NameValidity.INVALID) { 053 log.error("Invalid Sensor system Name format: {}", systemName); 054 throw new IllegalArgumentException("Invalid Sensor System Name format: " + systemName); 055 } 056 Sensor s; 057 String sName = systemName; 058 if (sName.isEmpty()) { 059 // system name is not valid 060 throw new IllegalArgumentException("Invalid Acela Sensor system name - " + // NOI18N 061 systemName); 062 } 063 // does this Sensor already exist 064 s = getBySystemName(sName); 065 if (s != null) { 066 throw new IllegalArgumentException("Acela Sensor with this name already exists - " + // NOI18N 067 systemName); 068 } 069 // check under alternate name 070 String altName = AcelaAddress.convertSystemNameToAlternate(sName, getSystemPrefix()); 071 s = getBySystemName(altName); 072 if (s != null) { 073 throw new IllegalArgumentException("Acela Sensor with name " + // NOI18N 074 systemName + " already exists as " + altName); 075 } 076 // check bit number 077 int bit = AcelaAddress.getBitFromSystemName(sName, getSystemPrefix()); 078 if ((bit < AcelaAddress.MINSENSORADDRESS) || (bit > AcelaAddress.MAXSENSORADDRESS)) { 079 log.error("Sensor bit number {} is outside the supported range {}-{}", bit, AcelaAddress.MINSENSORADDRESS, AcelaAddress.MAXSENSORADDRESS); 080 throw new IllegalArgumentException("Sensor bit number " + // NOI18N 081 Integer.toString(bit) + " is outside the supported range " + // NOI18N 082 Integer.toString(AcelaAddress.MAXSENSORADDRESS) + "-" + 083 Integer.toString(AcelaAddress.MAXSENSORADDRESS)); 084 } 085 // Sensor system name is valid and Sensor doesn't exist, make a new one 086 if (userName == null) { 087 s = new AcelaSensor(sName); 088 } else { 089 s = new AcelaSensor(sName, userName); 090 } 091 092 // ensure that a corresponding Acela Node exists 093 AcelaNode node = AcelaAddress.getNodeFromSystemName(sName, getMemo()); 094 if (node == null) { 095 log.warn("Sensor: {} refers to an undefined Acela Node.", sName); 096 return s; 097 } 098 if (!node.hasActiveSensors) { 099 int newNodeAddress; 100 newNodeAddress = node.getNodeAddress(); 101 log.warn("We got the wrong node: {}", newNodeAddress); 102 return s; 103 } 104 // register this sensor with the Acela Node 105 node.registerSensor(s, bit); 106 return s; 107 } 108 109 /** 110 * {@inheritDoc} 111 * <p> 112 * Verifies system name has valid prefix and is an integer from 113 * {@value AcelaAddress#MINSENSORADDRESS} to 114 * {@value AcelaAddress#MAXSENSORADDRESS}. 115 */ 116 @Override 117 @Nonnull 118 public String validateSystemNameFormat(@Nonnull String systemName, @Nonnull Locale locale) { 119 return super.validateIntegerSystemNameFormat(systemName, 120 AcelaAddress.MINSENSORADDRESS, 121 AcelaAddress.MAXSENSORADDRESS, 122 locale); 123 } 124 125 /** 126 * {@inheritDoc} 127 */ 128 @Override 129 public NameValidity validSystemNameFormat(@Nonnull String systemName) { 130 return (AcelaAddress.validSystemNameFormat(systemName, 'S', getSystemPrefix())); 131 } 132 133 /** 134 * Dummy routine 135 */ 136 @Override 137 public void message(AcelaMessage r) { 138 log.warn("unexpected message"); 139 } 140 141 /** 142 * Process a reply to a poll of Sensors of one node. 143 */ 144 @Override 145 public void reply(AcelaReply r) { 146 // Determine which state we are in: Initializing Acela Network or Polling Sensors 147 boolean currentstate = getMemo().getTrafficController().getAcelaTrafficControllerState(); 148 // Flag to indicate which state we are in: 149 // false == Initializing Acela Network 150 // true == Polling Sensors 151 if (!currentstate) { 152 int replysize = r.getNumDataElements(); 153 if (replysize == 0) { 154 // The Acela Online command seems to return an empty message 155 log.warn("We got an empty reply of size: {}", replysize); 156 } else { 157 if (replysize == 1) { 158 byte replyvalue = (byte) (r.getElement(0)); 159 if (replyvalue == 0x00) { 160 // Everything is OK. 161 } else { // Assume this is the response to the pollnodes 162 log.warn("We got a bad return code: {}", replyvalue); 163 } 164 } else { 165 for (int i = 0; i < replysize; i++) { 166 byte replynodetype = (byte) (r.getElement(i)); 167 168 int nodetype; 169 switch (replynodetype) { 170 case 0x00: { 171 nodetype = AcelaNode.AC; // Should never get this 172 break; 173 } 174 case 0x01: { 175 nodetype = AcelaNode.TB; 176 break; 177 } 178 case 0x02: { 179 nodetype = AcelaNode.D8; 180 break; 181 } 182 case 0x03: { 183 nodetype = AcelaNode.WM; 184 break; 185 } 186 case 0x04: { 187 nodetype = AcelaNode.SM; 188 break; 189 } 190 case 0x05: { 191 nodetype = AcelaNode.SC; 192 break; 193 } 194 case 0x06: { 195 nodetype = AcelaNode.SW; 196 break; 197 } 198 case 0x07: { 199 nodetype = AcelaNode.YM; 200 break; 201 } 202 case 0x08: { 203 nodetype = AcelaNode.SY; 204 break; 205 } 206 default: { 207 nodetype = AcelaNode.UN; // Should never get this 208 } 209 } 210 int tempaddr = i + 1; 211 new AcelaNode(tempaddr, nodetype, getMemo().getTrafficController()); 212 log.info("Created a new Acela Node [{}] as a result of Acela network Poll of type: {}", tempaddr, replynodetype); 213 } 214 getMemo().getTrafficController().setAcelaTrafficControllerState(true); 215 } 216 } 217 } else { 218 int replysize = r.getNumDataElements(); 219 if (replysize > 1) { // Bob C: not good if only one sensor module !! 220 getMemo().getTrafficController().updateSensorsFromPoll(r); 221 } 222 } 223 } 224 225 /** 226 * Register any orphan Sensors when a new Acela Node is created. 227 * @param node which node to search for sensors. 228 */ 229 public void registerSensorsForNode(AcelaNode node) { 230 // get list containing all Sensors 231 log.info("Trying to register sensor from Manager 2: {}Sxx", getSystemPrefix()); // multichar prefix 232 // Iterate through the sensors 233 AcelaNode tNode; 234 for (Sensor s : getNamedBeanSet()) { 235 String sName = s.getSystemName(); 236 log.debug("system Name is {}", sName); 237 if (sName.startsWith(getSystemNamePrefix())) { // multichar prefix 238 // This is an Acela Sensor 239 tNode = AcelaAddress.getNodeFromSystemName(sName, getMemo()); 240 if (tNode == node) { 241 // This sensor is for this new Acela Node - register it 242 node.registerSensor(s, 243 AcelaAddress.getBitFromSystemName(sName, getSystemPrefix())); 244 } 245 } 246 } 247 } 248 249 @Override 250 public boolean allowMultipleAdditions(@Nonnull String systemName) { 251 return true; 252 } 253 254 private final static Logger log = LoggerFactory.getLogger(AcelaSensorManager.class); 255 256}