/*********************************************************************************** * * Copyright (c) 2014 Kamil Baczkowicz * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v1.0 which accompany this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * * The Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * * Kamil Baczkowicz - initial API and implementation and/or initial documentation * */ package pl.baczkowicz.mqttspy.ui.controlpanel; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Random; import javafx.application.Platform; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.Node; import javafx.scene.control.Button; import javafx.scene.control.Hyperlink; import javafx.scene.control.Label; import pl.baczkowicz.mqttspy.stats.StatisticsManager; import pl.baczkowicz.mqttspy.ui.ControlPanelItemController; import pl.baczkowicz.mqttspy.ui.events.ShowExternalWebPageEvent; import pl.baczkowicz.spy.eventbus.IKBus; import pl.baczkowicz.spy.formatting.FormattingUtils; import pl.baczkowicz.spy.utils.ThreadingUtils; import pl.baczkowicz.spy.utils.TimeUtils; /** * Class responsible for updating control panel statistics. */ public class ControlPanelStatsUpdater implements Runnable { /** Milliseconds since first stats recorded. */ private final static long MILLISECONDS = new Date().getTime() - StatisticsManager.stats.getStartDate().toGregorianCalendar().getTime().getTime(); /** Days since first stats recorded. */ private final static long DAYS = MILLISECONDS / (1000 * 60 * 60 * 24); /** Text saying "in X days" or "" when only 1 day since first recorded stats. */ private final static String IN_DAYS_PHRASE = DAYS > 1 ? (" in " + DAYS + " days") : ""; /** Text saying "since XXXX-XX-XX", where the X is the date of first recorded stats. */ private final static String SINCE_PHRASE = " since " + TimeUtils.DATE_SDF.format(StatisticsManager.stats.getStartDate().toGregorianCalendar().getTime()); /** How many different stat messages there are. */ private final static int STATS_MESSAGES = 6; /** How many intervals allow before going to next stats. */ private final static int GO_NEXT_AFTER_INTERVALS = 10; /** The one second interval. */ private final static int ONE_SECOND_INTERVAL = 1000; /** Flag indicating whether the stats are being played automatically (true) or have been paused (false). */ private boolean statsPlaying; /** List of getting involved messages. */ private List<String> gettingInvolvedDetails = new ArrayList<String>(Arrays.asList( "Finding mqtt-spy useful? See how you can make mqtt-spy even better", "Like your mqtt-spy? See how you can help at", "Using mqtt-spy on a regular basis? See how you can help at")); /** The controller of the stats control panel item. */ private final ControlPanelItemController controlPanelItemController; /** Reference to the event bus. */ private IKBus eventBus; /** The button on which stats are displayed. */ private final Button bigButton; /** Counts how many seconds lapsed. */ private int secondCounter; /** The index of the current statistics message. */ private int statMessageIndex; public ControlPanelStatsUpdater(final ControlPanelItemController controlPanelItemController, final Button bigButton, final IKBus eventBus) { this.controlPanelItemController = controlPanelItemController; this.bigButton = bigButton; this.eventBus = eventBus; } /** * Displays the initial state of the statistics control panel item. */ public void show() { // Default values controlPanelItemController.setTitle("Connect to an MQTT broker to start seeing processing statistics..."); controlPanelItemController.setDetails(""); controlPanelItemController.setStatus(ItemStatus.STATS); bigButton.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { moveToNextStatMessage(); } }); final List<Node> items = new ArrayList<Node>(); // Getting involved details final Random r = new Random(); items.add(new Label(gettingInvolvedDetails.get(r.nextInt(gettingInvolvedDetails.size())))); final Hyperlink getInvolved = new Hyperlink(); getInvolved.setText("http://github.com/kamilfb/mqtt-spy/wiki/Getting-involved"); getInvolved.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { eventBus.publish(new ShowExternalWebPageEvent("http://github.com/kamilfb/mqtt-spy/wiki/Getting-involved")); } }); items.add(getInvolved); items.add(new Label(":)")); controlPanelItemController.getDetails().getChildren().addAll(items); statsPlaying = true; ControlPanelItemController.setButtonProperties(controlPanelItemController.getButton1(), "pause", true, new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { if (statsPlaying) { ControlPanelItemController.setButtonProperties(controlPanelItemController.getButton1(), "play", true); statsPlaying = false; } else { ControlPanelItemController.setButtonProperties(controlPanelItemController.getButton1(), "pause", true); statsPlaying = true; } event.consume(); } }); controlPanelItemController.refresh(); new Thread(this).start(); } /** * Moves to the next statistics message. */ private void moveToNextStatMessage() { secondCounter = 0; statMessageIndex++; if (statMessageIndex == STATS_MESSAGES) { statMessageIndex = 0; } // Try to update the stats - if all unavailable, ignore int retries = 0; while (!refreshStatsMessage(false) && retries < STATS_MESSAGES) { statMessageIndex++; if (statMessageIndex == STATS_MESSAGES) { statMessageIndex = 0; } retries++; } } /** * Refreshes the stats message. * * @param updateOnly Flag allowing some values to reach 0 and still be displayed * * @return True if the stats message has been updated */ private boolean refreshStatsMessage(final boolean updateOnly) { if ((statMessageIndex == 0) && (StatisticsManager.stats.getConnections() > 0)) { controlPanelItemController.setTitle(String.format( "Your mqtt-spy made %s connection" + (StatisticsManager.stats.getConnections() > 1 ? "s" : "") + " to MQTT brokers%s.", FormattingUtils.formatNumber(StatisticsManager.stats.getConnections()), IN_DAYS_PHRASE)); return true; } else if ((statMessageIndex == 1) && (StatisticsManager.stats.getMessagesPublished() > 1)) { controlPanelItemController.setTitle(String.format( "Your mqtt-spy published %s messages to MQTT brokers.", FormattingUtils.formatNumber(StatisticsManager.stats.getMessagesPublished()), IN_DAYS_PHRASE)); return true; } else if ((statMessageIndex == 2) && (StatisticsManager.stats.getSubscriptions() > 1)) { controlPanelItemController.setTitle(String.format( "Your mqtt-spy made %s subscriptions to MQTT brokers%s.", FormattingUtils.formatNumber(StatisticsManager.stats.getSubscriptions()), IN_DAYS_PHRASE)); return true; } else if ((statMessageIndex == 3) && (StatisticsManager.stats.getMessagesReceived() > 1)) { controlPanelItemController.setTitle(String.format( "Your mqtt-spy received %s messages%s.", FormattingUtils.formatNumber(StatisticsManager.stats.getMessagesReceived()), SINCE_PHRASE)); return true; } else if ((statMessageIndex == 4) && (updateOnly || StatisticsManager.getMessagesPublished() > 1)) { controlPanelItemController.setTitle(String.format( "Right now your mqtt-spy is publishing %s msgs/s.", StatisticsManager.getMessagesPublished())); return true; } else if ((statMessageIndex == 5) && (updateOnly || StatisticsManager.getMessagesReceived() > 1)) { controlPanelItemController.setTitle(String.format( "Right now your mqtt-spy is munching through %d msgs/s.", StatisticsManager.getMessagesReceived())); return true; } return false; } @Override public void run() { ThreadingUtils.logThreadStarting("Control Panel Stats Updater"); secondCounter = 0; while (true) { secondCounter++; oneCycleRefresh(); oneCycleMoveToNext(); if (ThreadingUtils.sleep(ONE_SECOND_INTERVAL)) { break; } } ThreadingUtils.logThreadEnding(); } /** * Refreshes currently displayed information. */ private void oneCycleRefresh() { Platform.runLater(new Runnable() { @Override public void run() { refreshStatsMessage(true); } }); } /** * When due, moves to the next stats message. */ private void oneCycleMoveToNext() { if (statsPlaying) { if (secondCounter == GO_NEXT_AFTER_INTERVALS) { secondCounter = 0; Platform.runLater(new Runnable() { @Override public void run() { moveToNextStatMessage(); } }); } } else { secondCounter = 0; } } }