001package jmri.jmrix.roco.z21; 002 003import java.util.Locale; 004 005import jmri.JmriException; 006import jmri.Sensor; 007import org.slf4j.Logger; 008import org.slf4j.LoggerFactory; 009 010import javax.annotation.Nonnull; 011 012/** 013 * Manage the Z21Specific Sensor implementation. 014 * <p> 015 * for RM Bus sensors, System names are "ZSnnn", where Z is the 016 * user-configurable system prefix and nnn is the sensor number without padding. 017 * <p> 018 * for CAN Bus sensors, System names are "ZSmm:pp" where Z is the 019 * user-configurable system prefix, mm is the CAN bus module id and pp is the 020 * contact number. 021 * 022 * @author Paul Bender Copyright (C) 2003-2018 023 * @navassoc 1 - * jmri.jmrix.lenz.Z21RMBusSensor 024 * @navassoc 1 - * jmri.jmrix.lenz.Z21CanSensor 025 */ 026public class Z21SensorManager extends jmri.managers.AbstractSensorManager implements Z21Listener { 027 028 // ctor has to register for Z21 events 029 public Z21SensorManager(Z21SystemConnectionMemo memo) { 030 super(memo); 031 // register for messages 032 memo.getTrafficController().addz21Listener(this); 033 // make sure we are going to get can detector and RMBus data from 034 // the command station 035 // set the broadcast flags so we get messages we may want to hear 036 memo.getRocoZ21CommandStation().setCanDetectorFlag(true); 037 memo.getRocoZ21CommandStation().setRMBusMessagesFlag(true); 038 // and forward the flags to the command station. 039 memo.getTrafficController().sendz21Message(Z21Message.getLanSetBroadcastFlagsRequestMessage( 040 memo.getRocoZ21CommandStation().getZ21BroadcastFlags()), null); 041 } 042 043 /** 044 * {@inheritDoc} 045 */ 046 @Override 047 @Nonnull 048 public Z21SystemConnectionMemo getMemo() { 049 return (Z21SystemConnectionMemo) memo; 050 } 051 052 // to free resources when no longer used 053 @Override 054 public void dispose() { 055 getMemo().getTrafficController().removez21Listener(this); 056 super.dispose(); 057 } 058 059 // Z21 specific methods 060 061 /** 062 * {@inheritDoc} 063 * <p> 064 * Assumes calling method has checked that a Sensor with this system 065 * name does not already exist. 066 * 067 * @throws IllegalArgumentException if the system name is not in a valid format 068 */ 069 @Override 070 @Nonnull 071 protected Sensor createNewSensor(@Nonnull String systemName, String userName) throws IllegalArgumentException { 072 if (systemName.contains(":")) { 073 // check for CAN format. 074 int bitNum = Z21CanBusAddress.getBitFromSystemName(systemName, getSystemPrefix()); 075 if (bitNum != -1) { 076 return new Z21CanSensor(systemName, userName, getMemo()); 077 } else { 078 throw new IllegalArgumentException("Invalid Sensor name: " + systemName); 079 } 080 } else { 081 // check if the output bit is available 082 int bitNum = Z21RMBusAddress.getBitFromSystemName(systemName, getSystemPrefix()); 083 if (bitNum != -1) { 084 // create the new RMBus Sensor object 085 return new Z21RMBusSensor(systemName, userName, 086 getMemo().getTrafficController(), getSystemPrefix()); 087 } else { 088 throw new IllegalArgumentException("Invalid Sensor name: " + systemName); 089 } 090 } 091 } 092 093 /** 094 * {@inheritDoc} 095 */ 096 @Override 097 public void reply(Z21Reply msg) { 098 log.debug("received message: {}", msg); 099 // LAN_CAN_DETECTOR message are related to CAN reporters/sensors. 100 if (msg.isCanDetectorMessage()) { 101 int type = (msg.getElement(9) & 0xFF); 102 log.debug("Sensor message type {}", type); 103 if (type == 0x01) { 104 log.debug("Received LAN_CAN_DETECTOR message"); 105 int netID = (msg.getElement(4) & 0xFF) + ((msg.getElement(5) & 0xFF) << 8); 106 int msgPort = (msg.getElement(8) & 0xFF); 107 int address = (msg.getElement(6) & 0xFF) + ((msg.getElement(7) & 0xFF) << 8); 108 String systemName = Z21CanBusAddress.buildDecimalSystemNameFromParts(getSystemPrefix(),typeLetter(),address,msgPort); 109 Z21CanSensor r = (Z21CanSensor) getBySystemName(systemName); 110 if (null == r) { 111 // try with the module's CAN network ID 112 systemName = Z21CanBusAddress.buildHexSystemNameFromParts(getSystemPrefix(),typeLetter(),netID, msgPort); 113 r = (Z21CanSensor) getBySystemName(systemName); 114 if (null == r) { 115 log.debug("Creating reporter {}", systemName); 116 // need to create a new one, and send the message on 117 // to the newly created object. 118 ((Z21CanSensor) provideSensor(systemName)).reply(msg); 119 } 120 } 121 } 122 } else if (msg.isRMBusDataChangedReply()) { 123 log.debug("Received RM Bus Data Changed message"); 124 // we could create sensors here automatically, but the 125 // feed response contains data for 80 sensors, with no way 126 // to tell which of the 80 are actually connected. 127 } 128 } 129 130 /** 131 * {@inheritDoc} 132 */ 133 @Override 134 public void message(Z21Message l) { 135 // no processing of outgoing messages. 136 } 137 138 /** 139 * {@inheritDoc} 140 */ 141 @Override 142 @Nonnull 143 public String validateSystemNameFormat(@Nonnull String name, @Nonnull Locale locale) { 144 name = validateSystemNamePrefix(name, locale); 145 if (name.substring(getSystemNamePrefix().length()).contains(":")) { 146 return Z21CanBusAddress.validateSystemNameFormat(name, this, locale); 147 } else { 148 return Z21RMBusAddress.validateSystemNameFormat(name, this, locale); 149 } 150 } 151 152 /** 153 * {@inheritDoc} 154 */ 155 @Override 156 public NameValidity validSystemNameFormat(@Nonnull String systemName) { 157 return Z21RMBusAddress.validSystemNameFormat(systemName, 'S', getSystemPrefix()) == NameValidity.VALID 158 ? NameValidity.VALID 159 : Z21CanBusAddress.validSystemNameFormat(systemName, 'S', getSystemPrefix()); 160 } 161 162 @Override 163 public boolean allowMultipleAdditions(@Nonnull String systemName) { 164 return true; 165 } 166 167 @Override 168 @Nonnull 169 public synchronized String createSystemName(@Nonnull String curAddress, @Nonnull String prefix) throws JmriException { 170 int encoderAddress; 171 int input; 172 173 if (curAddress.contains(":")) { 174 // This is a CAN Bus sensor address passed in the form of encoderAddress:input 175 int seperator = curAddress.indexOf(':'); 176 try { 177 encoderAddress = Integer.parseInt(curAddress.substring(0, seperator)); 178 input = Integer.parseInt(curAddress.substring(seperator + 1)); 179 return Z21CanBusAddress.buildDecimalSystemNameFromParts(getSystemPrefix(),typeLetter(),encoderAddress,input); 180 } catch (NumberFormatException ex) { 181 // system name may include hex values for CAN sensors. 182 try { 183 encoderAddress = Integer.parseInt(curAddress.substring(0, seperator), 16); 184 input = Integer.parseInt(curAddress.substring(seperator + 1)); 185 return Z21CanBusAddress.buildHexSystemNameFromParts(getSystemPrefix(),typeLetter(),encoderAddress,input); 186 } catch (NumberFormatException ex1) { 187 throw new JmriException("Unable to convert "+curAddress+" into the cab and input format of nn:xx"); 188 } 189 } 190 } else { 191 // This is an RMBus Sensor address. 192 try { 193 iName = Integer.parseInt(curAddress); 194 return getSystemPrefix() + typeLetter() + iName; 195 } catch (NumberFormatException ex) { 196 throw new JmriException("Hardware Address "+curAddress+" passed should be a number or the cab and input format of nn:xx"); 197 } 198 } 199 } 200 201 int iName; // must synchronize to avoid race conditions. 202 203 /** 204 * {@inheritDoc} 205 */ 206 @Override 207 public Sensor getBySystemName(@Nonnull String sName){ 208 Z21SystemNameComparator comparator = new Z21SystemNameComparator(getSystemPrefix(),typeLetter()); 209 return getBySystemName(sName,comparator); 210 } 211 212 /** 213 * Provide a manager-specific tooltip for the Add new item beantable pane. 214 */ 215 @Override 216 public String getEntryToolTip() { 217 return Bundle.getMessage("AddInputEntryToolTip"); 218 } 219 220 private static final Logger log = LoggerFactory.getLogger(Z21SensorManager.class); 221 222}