001package jmri.jmrix.roco.z21;
002
003import jmri.*;
004
005import java.util.Collection;
006import java.util.HashSet;
007
008/**
009 * Z21Reporter implements the Reporter Manager interface
010 * for Roco Z21 systems.
011 * <p>
012 * Reports from this reporter are of the type jmri.RailCom.
013 *
014 * @author Paul Bender Copyright (C) 2016
015 */
016public class Z21Reporter extends jmri.implementation.AbstractRailComReporter implements Z21Listener, CollectingReporter {
017
018    private Z21SystemConnectionMemo _memo;
019
020    private final HashSet<Object> entrySet;
021
022    private javax.swing.Timer refreshTimer; // Timer used to periodically
023    // referesh the RailCom data (this does not appear to happen automatically).
024    private static final int refreshTimeoutValue = 15000; 
025
026    /**  
027     * Create a new Z21Reporter.
028     *
029     * @param systemName the system name of the new reporter.
030     * @param userName the user name of the new reporter.
031     * @param memo an instance of Z21SystemConnectionMemo this manager 
032     *             is associated with.
033     *
034     */
035    public Z21Reporter(String systemName,String userName,Z21SystemConnectionMemo memo){
036        super(systemName,userName);
037        _memo = memo;
038        _memo.getTrafficController().addz21Listener(this);
039        entrySet = new HashSet<>();
040        // request an update from the layout.
041       requestUpdateFromLayout();
042       refreshTimer();
043    }
044
045    /**
046     * {@inheritDoc}
047     */
048    @Override
049    public boolean hasExitReports() {
050        return true;
051    }
052
053    /**
054     *     request an update from the layout.
055     */
056    private void requestUpdateFromLayout(){
057       _memo.getTrafficController().sendz21Message(Z21Message.getLanRailComGetDataRequestMessage(),this);
058    }
059
060    // the Z21 Listener interface
061
062    /**
063     * Member function that will be invoked by a z21Interface implementation to
064     * forward a z21 message from the layout.
065     *
066     * @param msg The received z21 reply. Note that this same object may be
067     * presented to multiple users. It should not be modified here.
068     */
069    @Override
070    public void reply(Z21Reply msg){
071         // for incoming messages all the reporter cares about is
072         // LAN_RAILCOM_DATACHANGED messages.
073         if(msg.isRailComDataChangedMessage()){
074             // find out how many RailCom Transmitters the command
075             // station is telling us about (there is a maximum of 19).
076             int tags = msg.getNumRailComDataEntries();
077             for(int i=0;i<tags;i++){
078                 // get the locomotive address from the message.
079                 DccLocoAddress l = msg.getRailComLocoAddress(i);
080                 // see if there is a tag for this address.
081                 RailCom tag = (RailCom) InstanceManager.getDefault(RailComManager.class).provideIdTag("" + l.getNumber());
082                 tag.setActualSpeed(msg.getRailComSpeed(i));
083                 notify(tag);
084                 if(!entrySet.contains(tag)){
085                     entrySet.add(tag);
086                 }
087             }
088             if(tags == 0){
089                 notify(null); // clear the current report if no tags.
090                 entrySet.clear();
091             }
092         }
093    }
094
095    /**
096     * Member function that will be invoked by a z21Interface implementation to
097     * forward a z21 message sent to the layout. Normally, this function will do
098     * nothing.
099     *
100     * @param msg The received z21 message. Note that this same object may be
101     * presented to multiple users. It should not be modified here.
102     */
103    @Override
104    public void message(Z21Message msg){
105         // we don't need to handle outgoing messages, so just ignore them.
106    }
107
108    /**
109     * Set up the refreshTimer, and start it.
110     */
111    private void refreshTimer() {
112        if (refreshTimer == null) {
113            refreshTimer = new javax.swing.Timer(refreshTimeoutValue, e -> {
114                // If the timer times out, send a request for status
115                requestUpdateFromLayout();
116            });
117        }
118        refreshTimer.stop();
119        refreshTimer.setInitialDelay(refreshTimeoutValue);
120        refreshTimer.setRepeats(true);
121        refreshTimer.start();
122    }
123
124    @Override
125    public Collection<Object> getCollection() {
126        return entrySet;
127    }
128
129    @Override
130    public void dispose(){
131        super.dispose();
132        refreshTimer.stop();
133    }
134
135}