001package jmri.jmrix.lenz; 002 003import java.util.Locale; 004import javax.annotation.Nonnull; 005import jmri.JmriException; 006import jmri.NamedBean; 007import jmri.Sensor; 008import org.slf4j.Logger; 009import org.slf4j.LoggerFactory; 010 011/** 012 * Manage the XpressNet specific Sensor implementation. 013 * <p> 014 * System names are "XSnnn", where X is the user configurable system prefix, 015 * nnn is the sensor number without padding. 016 * 017 * @author Paul Bender Copyright (C) 2003-2010 018 * @navassoc 1 - * jmri.jmrix.lenz.XNetSensor 019 */ 020public class XNetSensorManager extends jmri.managers.AbstractSensorManager implements XNetListener { 021 022 // ctor has to register for XNet events 023 public XNetSensorManager(XNetSystemConnectionMemo memo) { 024 super(memo); 025 tc = memo.getXNetTrafficController(); 026 tc.addXNetListener(XNetInterface.FEEDBACK, this); 027 } 028 029 protected XNetTrafficController tc; 030 031 /** 032 * {@inheritDoc} 033 */ 034 @Override 035 @Nonnull 036 public XNetSystemConnectionMemo getMemo() { 037 return (XNetSystemConnectionMemo) memo; 038 } 039 040 // to free resources when no longer used 041 @Override 042 public void dispose() { 043 tc.removeXNetListener(XNetInterface.FEEDBACK, this); 044 super.dispose(); 045 } 046 047 // XpressNet specific methods 048 049 /** 050 * {@inheritDoc} 051 * <p> 052 * Assumes calling method has checked that a Sensor with this 053 * system name does not already exist. 054 * 055 * @throws IllegalArgumentException when SystemName can't be converted 056 */ 057 @Override 058 @Nonnull 059 protected Sensor createNewSensor(@Nonnull String systemName, String userName) throws IllegalArgumentException { 060 // check if the output bit is available 061 int bitNum = XNetAddress.getBitFromSystemName(systemName, getSystemPrefix()); 062 if (bitNum == -1) { 063 throw new IllegalArgumentException("Unable to convert " + // NOI18N 064 systemName + 065 " to XNet sensor address"); // NOI18N 066 } 067 // normalize system name 068 String sName = getSystemNamePrefix() + bitNum; 069 // create the new Sensor object 070 return new XNetSensor(sName, userName, tc, getSystemPrefix()); 071 } 072 073 // listen for sensors, creating them as needed 074 @Override 075 public void message(XNetReply l) { 076 log.debug("received message: {}", l); 077 if (l.isFeedbackBroadcastMessage()) { 078 int numDataBytes = l.getElement(0) & 0x0f; 079 for (int i = 1; i < numDataBytes; i += 2) { 080 if (l.getFeedbackMessageType(i) == 2) { 081 // This is a feedback encoder message. The address of the 082 // Feedback sensor is byte two of the message. 083 int address = l.getFeedbackEncoderMsgAddr(i); 084 log.debug("Message for feedback encoder {}", address); 085 086 int firstaddress = ((address) * 8) + 1; 087 // Each Feedback encoder includes 8 addresses, so register 088 // a sensor for each address. 089 for (int j = 0; j < 8; j++) { 090 String s = getSystemNamePrefix() + (firstaddress + j); 091 if (null == getBySystemName(s)) { 092 // The sensor doesn't exist. We need to create a 093 // new sensor, and forward this message to it. 094 ((XNetSensor) provideSensor(s)).initmessage(l); 095 } else { 096 // The sensor exists. We need to forward this 097 // message to it. 098 Sensor xns = getBySystemName(s); 099 if (xns == null) { 100 log.error("Failed to get sensor for {}", s); 101 } else { 102 ((XNetSensor) xns).message(l); 103 } 104 } 105 } 106 } 107 } 108 } 109 } 110 111 /** 112 * Listen for the messages to the LI100/LI101. 113 */ 114 @Override 115 public void message(XNetMessage l) { 116 } 117 118 /** 119 * Handle a timeout notification. 120 */ 121 @Override 122 public void notifyTimeout(XNetMessage msg) { 123 if (log.isDebugEnabled()) { 124 log.debug("Notified of timeout on message{}", msg.toString()); 125 } 126 } 127 128 /** 129 * {@inheritDoc} 130 */ 131 @Override 132 @Nonnull 133 public String validateSystemNameFormat(@Nonnull String name, @Nonnull Locale locale) { 134 if (name.contains(":")) { 135 validateSystemNamePrefix(name, locale); 136 String[] parts = name.substring(getSystemNamePrefix().length()).split(":"); 137 if (parts.length != 2) { 138 throw new NamedBean.BadSystemNameException( 139 Bundle.getMessage(Locale.ENGLISH, "SystemNameInvalidAddress", name), 140 Bundle.getMessage(locale, "SystemNameInvalidAddress", name)); 141 } 142 try { 143 int address = Integer.parseInt(parts[0]); 144 if (address < 1 || address > 127) { 145 throw new NamedBean.BadSystemNameException( 146 Bundle.getMessage(Locale.ENGLISH, "SystemNameInvalidModule", name, parts[0]), 147 Bundle.getMessage(locale, "SystemNameInvalidModule", name, parts[0])); 148 } 149 } catch (NumberFormatException ex) { 150 throw new NamedBean.BadSystemNameException( 151 Bundle.getMessage(Locale.ENGLISH, "SystemNameInvalidModule", name, parts[0]), 152 Bundle.getMessage(locale, "SystemNameInvalidModule", name, parts[0])); 153 } 154 try { 155 int bit = Integer.parseInt(parts[1]); 156 if (bit < 1 || bit > 8) { 157 throw new NamedBean.BadSystemNameException( 158 Bundle.getMessage(Locale.ENGLISH, "SystemNameInvalidBit", name, parts[1]), 159 Bundle.getMessage(locale, "SystemNameInvalidBit", name, parts[1])); 160 } 161 } catch (NumberFormatException ex) { 162 throw new NamedBean.BadSystemNameException( 163 Bundle.getMessage(Locale.ENGLISH, "SystemNameInvalidBit", name, parts[1]), 164 Bundle.getMessage(locale, "SystemNameInvalidBit", name, parts[1])); 165 } 166 return name; 167 } else { 168 return validateIntegerSystemNameFormat(name, 169 XNetAddress.MINSENSORADDRESS, 170 XNetAddress.MAXSENSORADDRESS, 171 locale); 172 } 173 } 174 175 /** 176 * {@inheritDoc} 177 */ 178 @Override 179 public NameValidity validSystemNameFormat(@Nonnull String systemName) { 180 return (XNetAddress.validSystemNameFormat(systemName, 'S', getSystemPrefix())); 181 } 182 183 @Override 184 public boolean allowMultipleAdditions(@Nonnull String systemName) { 185 return true; 186 } 187 188 @Override 189 @Nonnull 190 synchronized public String createSystemName(@Nonnull String curAddress, @Nonnull String prefix) throws JmriException { 191 int encoderAddress; 192 int input; 193 194 if (curAddress.contains(":")) { 195 // Address format passed is in the form of encoderAddress:input or T:turnout address 196 int seperator = curAddress.indexOf(":"); 197 try { 198 encoderAddress = Integer.parseInt(curAddress.substring(0, seperator)); 199 input = Integer.parseInt(curAddress.substring(seperator + 1)); 200 } catch (NumberFormatException ex) { 201 throw new JmriException("Unable to convert "+curAddress+" into the cab and input format of nn:xx"); 202 } 203 iName = ((encoderAddress - 1) * 8) + input; 204 } else { 205 // Entered in using the old format 206 try { 207 iName = Integer.parseInt(curAddress); 208 } catch (NumberFormatException ex) { 209 throw new JmriException("Hardware Address "+curAddress+" should be a number or the cab and input format of nn:xx"); 210 } 211 } 212 return prefix + typeLetter() + iName; 213 } 214 215 private int iName; // must synchronize to avoid race conditions. 216 217 /** 218 * {@inheritDoc} 219 */ 220 @Override 221 public String getEntryToolTip() { 222 return Bundle.getMessage("AddInputEntryToolTip"); 223 } 224 225 private static final Logger log = LoggerFactory.getLogger(XNetSensorManager.class); 226 227}