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     *     request an update from the layout.
047     */
048    private void requestUpdateFromLayout(){
049       _memo.getTrafficController().sendz21Message(Z21Message.getLanRailComGetDataRequestMessage(),this);
050    }
051
052    // the Z21 Listener interface
053
054    /**
055     * Member function that will be invoked by a z21Interface implementation to
056     * forward a z21 message from the layout.
057     *
058     * @param msg The received z21 reply. Note that this same object may be
059     * presented to multiple users. It should not be modified here.
060     */
061    @Override
062    public void reply(Z21Reply msg){
063         // for incoming messages all the reporter cares about is
064         // LAN_RAILCOM_DATACHANGED messages.
065         if(msg.isRailComDataChangedMessage()){
066             // find out how many RailCom Transmitters the command
067             // station is telling us about (there is a maximum of 19).
068             int tags = msg.getNumRailComDataEntries();
069             for(int i=0;i<tags;i++){
070                 // get the locomotive address from the message.
071                 DccLocoAddress l = msg.getRailComLocoAddress(i);
072                 // see if there is a tag for this address.
073                 RailCom tag = (RailCom) InstanceManager.getDefault(RailComManager.class).provideIdTag("" + l.getNumber());
074                 tag.setActualSpeed(msg.getRailComSpeed(i));
075                 notify(tag);
076                 if(!entrySet.contains(tag)){
077                     entrySet.add(tag);
078                 }
079             }
080             if(tags == 0){
081                 notify(null); // clear the current report if no tags.
082                 entrySet.clear();
083             }
084         }
085    }
086
087    /**
088     * Member function that will be invoked by a z21Interface implementation to
089     * forward a z21 message sent to the layout. Normally, this function will do
090     * nothing.
091     *
092     * @param msg The received z21 message. Note that this same object may be
093     * presented to multiple users. It should not be modified here.
094     */
095    @Override
096    public void message(Z21Message msg){
097         // we don't need to handle outgoing messages, so just ignore them.
098    }
099
100    /**
101     * Set up the refreshTimer, and start it.
102     */
103    private void refreshTimer() {
104        if (refreshTimer == null) {
105            refreshTimer = new javax.swing.Timer(refreshTimeoutValue, e -> {
106                // If the timer times out, send a request for status
107                requestUpdateFromLayout();
108            });
109        }
110        refreshTimer.stop();
111        refreshTimer.setInitialDelay(refreshTimeoutValue);
112        refreshTimer.setRepeats(true);
113        refreshTimer.start();
114    }
115
116    @Override
117    public Collection<Object> getCollection() {
118        return entrySet;
119    }
120
121    @Override
122    public void dispose(){
123        super.dispose();
124        refreshTimer.stop();
125    }
126
127}