001
002package jmri.jmrit.withrottle;
003
004import java.beans.PropertyChangeListener;
005import java.util.TimeZone;
006
007import org.slf4j.Logger;
008import org.slf4j.LoggerFactory;
009
010import jmri.InstanceManager;
011import jmri.Timebase;
012
013/**
014 * Fast Clock interface for Wi-Fi throttles.
015 * <p>
016 * Fast Clock display on devices will be synchronized with hardware or software
017 * clock. Time is UTC seconds on Wi-Fi devices, Local milliseconds in JMRI.
018 *
019 * @author Brett Hoffman Copyright (C) 2018
020 */
021public class FastClockController extends AbstractController {
022
023    private final Timebase fastClock;
024    private final PropertyChangeListener timeAndRateListener;
025    
026    public FastClockController() {
027               
028        fastClock = InstanceManager.getDefault(jmri.Timebase.class);
029        timeAndRateListener = new PropertyChangeListener() {
030            @Override
031            public void propertyChange(java.beans.PropertyChangeEvent e) {
032                //skip minutes updates on this listener, handled in minuteListener
033                log.trace("timeAndRateListener propertyChange for '{}' from '{}' to '{}'",e.getPropertyName(),e.getOldValue(),e.getNewValue());                
034                if (!e.getPropertyName().equals("minutes")) {
035                    sendFastTimeAndRate();
036                }
037            }
038        };
039        
040        isValid = true;
041        
042        fastClock.addPropertyChangeListener(timeAndRateListener);
043    }
044    
045    @Override
046    boolean verifyCreation() {
047        return isValid;
048    }
049
050    @Override
051    void handleMessage(String message, DeviceServer deviceServer) {
052        throw new UnsupportedOperationException("Not used.");
053    }
054
055    @Override
056    void register() {
057        throw new UnsupportedOperationException("Not used.");
058    }
059
060    @Override
061    void deregister() {
062        // cancel callback to update time
063        fastClock.removePropertyChangeListener(timeAndRateListener);
064    }
065    
066    
067    /**
068     * Fast clock should not have a time zone.
069     * <p>
070     * Add the offset to give straight UTC value,
071     *  based on the fastclock's datetime
072     * @return Time, as UTC in seconds
073     */
074    private long getAdjustedTime() {
075        long fastClockTime = fastClock.getTime().getTime(); //fastclock time as milliseconds
076        int timeZoneOffset = TimeZone.getDefault().getOffset(fastClockTime); //timezone offset in milliseconds
077        return ((fastClockTime + timeZoneOffset) / 1000); //returned adjusted time in seconds
078    }
079       
080    /**
081     * Send Time and Rate.
082     * <p>
083     * Time on device will update to the value that is sent and rate will allow 
084     * Fast Clock to keep its own time. A rate == 0 will tell the device to 
085     * stop the clock.
086     */
087    public void sendFastTimeAndRate() {
088        //  Send the time and run rate whether running or not
089        if (listeners != null) {
090            for (ControllerInterface listener : listeners) {
091                listener.sendPacketToDevice("PFT" + getAdjustedTime() + "<;>" + fastClock.userGetRate());
092            }
093            if (!fastClock.getRun()) {
094                //  Not running, send rate of 0
095                //  This will stop a running clock without changing stored rate
096                for (ControllerInterface listener : listeners) {
097                    listener.sendPacketToDevice("PFT" + getAdjustedTime() + "<;>" + 0.0);
098                }
099            }
100        }
101    }
102
103    private final static Logger log = LoggerFactory.getLogger(FastClockController.class);
104}