001package jmri.jmrix.roco.z21; 002 003import javax.annotation.Nonnull; 004import jmri.InstanceManager; 005import jmri.RailComManager; 006import jmri.Reporter; 007import org.slf4j.Logger; 008import org.slf4j.LoggerFactory; 009 010/** 011 * Z21ReporterManager implements the Reporter Manager interface for Roco Z21 012 * systems. 013 * Todo : add validateSystemNameFormat method 014 * 015 * @author Paul Bender Copyright (C) 2016 016 */ 017public class Z21ReporterManager extends jmri.managers.AbstractReporterManager implements Z21Listener { 018 019 private boolean autoCreateInternalReporter = false; // disable automatic 020 // creation of the internal reporter 021 // by default. It interferes with 022 // reports from the Roco 10808 023 // which it preceeds in the circuit. 024 025 /** 026 * Create a new Z21ReporterManager 027 * 028 * @param memo an instance of Z21SystemConnectionMemo this manager is 029 * associated with. 030 */ 031 public Z21ReporterManager(Z21SystemConnectionMemo memo) { 032 super(memo); 033 try { 034 InstanceManager.getDefault(RailComManager.class); 035 } catch (NullPointerException npe) { 036 // there is no RailComManager, so create a new one 037 InstanceManager.setDefault(RailComManager.class, 038 new jmri.managers.DefaultRailComManager()); 039 } 040 // register for messages 041 memo.getTrafficController().addz21Listener(this); 042 // make sure we are going to get railcom data from the command station 043 // set the broadcast flags so we get messages we may want to hear 044 memo.getRocoZ21CommandStation().setRailComMessagesFlag(true); 045 memo.getRocoZ21CommandStation().setRailComAutomaticFlag(true); 046 memo.getRocoZ21CommandStation().setCanDetectorFlag(true); 047 // and forward the flags to the command station. 048 memo.getTrafficController().sendz21Message(Z21Message.getLanSetBroadcastFlagsRequestMessage( 049 memo.getRocoZ21CommandStation().getZ21BroadcastFlags()), null); 050 // And then send a message requesting an update from the hardware. 051 // This is required because the RailCom data currently requires polling. 052 memo.getTrafficController().sendz21Message(Z21Message.getLanRailComGetDataRequestMessage(), this); 053 } 054 055 /** 056 * {@inheritDoc} 057 */ 058 @Override 059 @Nonnull 060 public Z21SystemConnectionMemo getMemo() { 061 return (Z21SystemConnectionMemo) memo; 062 } 063 064 @Override 065 @Nonnull 066 protected Reporter createNewReporter(@Nonnull String systemName, String userName) throws IllegalArgumentException { 067 if (!systemName.matches(getSystemPrefix() + typeLetter() + "[" + 1 + "]")) { 068 int bitNum = Z21CanBusAddress.getBitFromSystemName(systemName, getSystemPrefix()); 069 if (bitNum != -1) { 070 Reporter r = new Z21CanReporter(systemName, userName, getMemo()); 071 register(r); 072 return r; 073 } else { 074 log.warn("Invalid Reporter name: {} ", systemName); 075 throw new IllegalArgumentException("Invalid Reporter name: " + systemName); 076 } 077 } 078 // Create and register the reporter 079 Reporter r = new Z21Reporter(systemName, userName, getMemo()); 080 register(r); 081 return r; 082 } 083 084 // the Z21 Listener interface 085 /** 086 * Member function that will be invoked by a z21Interface implementation to 087 * forward a z21 message from the layout. 088 * 089 * @param msg The received z21 reply. Note that this same object may be 090 * presented to multiple users. It should not be modified here. 091 */ 092 @Override 093 public void reply(Z21Reply msg) { 094 // LAN_RAILCOM_DATACHANGED messages are related to the built in 095 // reporter. 096 if (autoCreateInternalReporter && msg.isRailComDataChangedMessage()) { 097 log.debug("Received RailComDatachanged message"); 098 String systemName = getSystemPrefix() + typeLetter() + 1; 099 Z21Reporter r = (Z21Reporter) getBySystemName(systemName); // there is only one built in reporter. 100 if (null == r) { 101 log.debug("Creating reporter {}",systemName); 102 // need to create a new one, and send the message on 103 // to the newly created object. 104 ((Z21Reporter) provideReporter(systemName)).reply(msg); 105 } 106 // LAN_CAN_DETECTOR message are related to CAN reporters. 107 } else if (msg.isCanDetectorMessage()) { 108 int type = (msg.getElement(9) & 0xFF); 109 log.debug("reporter message type {}", type); 110 if (type >= 0x11 && type <= 0x1f) { 111 log.debug("Received LAN_CAN_DETECTOR message"); 112 int netID = (msg.getElement(4) & 0xFF) + ((msg.getElement(5) & 0xFF) << 8); 113 int msgPort = (msg.getElement(8) & 0xFF); 114 int address = (msg.getElement(6) & 0xFF) + ((msg.getElement(7) & 0xFF) << 8); 115 String systemName = Z21CanBusAddress.buildDecimalSystemNameFromParts(getSystemPrefix(),typeLetter(),address,msgPort); 116 log.debug("asking for reporter {}", systemName); 117 Z21CanReporter r = (Z21CanReporter) getBySystemName(systemName); 118 if (null == r) { 119 // try with the module's CAN network ID 120 systemName = Z21CanBusAddress.buildHexSystemNameFromParts(getSystemPrefix(),typeLetter(),netID,msgPort); 121 log.debug("not found; asking for reporter {}", systemName); 122 r = (Z21CanReporter) getBySystemName(systemName); 123 if (null == r) { 124 log.debug("Creating reporter {}", systemName); 125 // need to create a new one, and send the message on 126 // to the newly created object. 127 ((Z21CanReporter) provideReporter(systemName)).reply(msg); 128 } 129 } 130 } 131 } 132 } 133 134 /** 135 * Member function that will be invoked by a z21Interface implementation to 136 * forward a z21 message sent to the layout. Normally, this function will do 137 * nothing. 138 * 139 * @param msg The received z21 message. Note that this same object may be 140 * presented to multiple users. It should not be modified here. 141 */ 142 @Override 143 public void message(Z21Message msg) { 144 // we don't need to handle outgoing messages, so just ignore them. 145 } 146 147 /** 148 * Enable automatic creation of the Internal Z21 Reporter from messages. 149 * Defaults to disabled. 150 */ 151 public void enableInternalReporterCreationFromMessages() { 152 autoCreateInternalReporter = true; 153 } 154 155 /** 156 * {@inheritDoc} 157 */ 158 @Override 159 public Reporter getBySystemName(@Nonnull String sName){ 160 Z21SystemNameComparator comparator = new Z21SystemNameComparator(getSystemPrefix(),typeLetter()); 161 return getBySystemName(sName,comparator); 162 } 163 164 private static final Logger log = LoggerFactory.getLogger(Z21ReporterManager.class); 165 166}