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}