All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bidib.wizard.mvc.booster.controller.BoosterTableController Maven / Gradle / Ivy

There is a newer version: 2.0.29
Show newest version
package org.bidib.wizard.mvc.booster.controller;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.function.Supplier;

import javax.swing.Timer;

import org.apache.commons.collections4.CollectionUtils;
import org.bidib.jbidibc.messages.enums.CommandStationState;
import org.bidib.jbidibc.messages.utils.NodeUtils;
import org.bidib.jbidibc.messages.utils.ThreadFactoryBuilder;
import org.bidib.wizard.api.model.NodeInterface;
import org.bidib.wizard.api.model.NodeListProvider;
import org.bidib.wizard.api.model.NodeProvider;
import org.bidib.wizard.api.model.event.NodeStatusEvent.StatusIdentifier;
import org.bidib.wizard.api.model.listener.DefaultNodeListListener;
import org.bidib.wizard.api.model.listener.NodeListListener;
import org.bidib.wizard.api.service.console.ConsoleService;
import org.bidib.wizard.api.service.node.BoosterService;
import org.bidib.wizard.api.service.node.CommandStationService;
import org.bidib.wizard.client.common.uils.SwingUtils;
import org.bidib.wizard.client.common.view.DockKeys;
import org.bidib.wizard.client.common.view.DockUtils;
import org.bidib.wizard.client.common.view.statusbar.StatusBar;
import org.bidib.wizard.common.labels.WizardLabelWrapper;
import org.bidib.wizard.core.model.connection.ConnectionRegistry;
import org.bidib.wizard.core.service.ConnectionService;
import org.bidib.wizard.model.status.BoosterStatus;
import org.bidib.wizard.model.status.CommandStationStatus;
import org.bidib.wizard.mvc.booster.controller.listener.BoosterTableControllerListener;
import org.bidib.wizard.mvc.booster.model.BoosterModel;
import org.bidib.wizard.mvc.booster.model.BoosterTableModel;
import org.bidib.wizard.mvc.booster.view.BoosterTableView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.vlsolutions.swing.docking.Dockable;
import com.vlsolutions.swing.docking.DockableState;
import com.vlsolutions.swing.docking.DockingConstants;
import com.vlsolutions.swing.docking.DockingDesktop;
import com.vlsolutions.swing.docking.DockingUtilities;
import com.vlsolutions.swing.docking.RelativeDockablePosition;
import com.vlsolutions.swing.docking.TabbedDockableContainer;
import com.vlsolutions.swing.docking.event.DockableStateChangeEvent;
import com.vlsolutions.swing.docking.event.DockableStateChangeListener;

public class BoosterTableController implements BoosterTableControllerListener {
    private static final Logger LOGGER = LoggerFactory.getLogger(BoosterTableController.class);

    private BoosterTableView boosterTableView;

    private BoosterTableModel boosterTableModel;

    private Timer boosterCurrentTimer;

    private static final long CURRENT_UPDATE_TIMEOUT = 3000;

    @Autowired
    private ConnectionService connectionService;

    @Autowired
    private BoosterService boosterService;

    @Autowired
    private CommandStationService commandStationService;

    @Autowired
    private StatusBar statusBar;

    @Autowired
    private WizardLabelWrapper wizardLabelWrapper;

    @Autowired
    private ConsoleService consoleService;

    private final DockingDesktop desktop;

    private DockableStateChangeListener dockableStateChangeListener;

    private final Supplier nodeProviderSupplier;

    private final NodeListProvider nodeListProvider;

    protected final ScheduledExecutorService serviceWorker;

    public BoosterTableController(final DockingDesktop desktop, final Supplier nodeProviderSupplier,
        final NodeListProvider nodeListProvider) {
        this.desktop = desktop;
        this.nodeProviderSupplier = nodeProviderSupplier;
        this.nodeListProvider = nodeListProvider;

        final ThreadFactory namedThreadFactory =
            new ThreadFactoryBuilder().setNameFormat("boosterServiceWorkers-thread-%d").build();
        serviceWorker = Executors.newScheduledThreadPool(2, namedThreadFactory);
    }

    public void start() {

        // check if the booster table view is already opened
        String searchKey = DockKeys.BOOSTER_TABLE_VIEW;
        LOGGER.info("Search for view with key: {}", searchKey);
        Dockable view = desktop.getContext().getDockableByKey(searchKey);
        if (view != null) {
            LOGGER.info("Select the existing booster table view.");
            DockUtils.selectWindow(view);
            return;
        }

        createDockable();
    }

    public Dockable createDockable() {

        LOGGER.info("Create new BoosterTableView.");

        if (boosterTableView != null) {
            LOGGER.info("Select the existing booster table view.");
            DockUtils.selectWindow(boosterTableView);
            return boosterTableView;
        }

        boosterTableModel =
            new BoosterTableModel((text, duration) -> statusBar.setStatusText(text, duration), consoleService);

        boosterTableView = new BoosterTableView(this, boosterTableModel);

        // add the log panel next to the console panel
        DockableState[] dockables = desktop.getDockables();
        LOGGER.info("Current dockables: {}", new Object[] { dockables });

        if (desktop.getDockables().length > 1) {

            DockableState consoleView = null;
            // search the console view
            for (DockableState dockable : dockables) {

                if (DockKeys.DOCKKEY_CONSOLE_VIEW.equals(dockable.getDockable().getDockKey())) {
                    LOGGER.info("Found the console view dockable.");
                    consoleView = dockable;

                    break;
                }
                else if (DockKeys.DOCKKEY_DEBUG_CONSOLE_VIEW.equals(dockable.getDockable().getDockKey())) {
                    LOGGER.info("Found the debug console view dockable.");
                    consoleView = dockable;

                    break;
                }
            }

            Dockable dock = desktop.getDockables()[1].getDockable();

            if (consoleView != null) {
                LOGGER.info("Add the booster table view to the console view panel.");
                dock = consoleView.getDockable();

                int order = 0;

                LOGGER.info("Add new booster table at order: {}", order);

                desktop.createTab(dock, boosterTableView, order, true);
                TabbedDockableContainer baseTab = DockingUtilities.findTabbedDockableContainer(dock);
                baseTab.removeDockable(boosterTableView);
                baseTab.addDockable(boosterTableView, order);
                baseTab.setSelectedDockable(boosterTableView);
            }
            else if (desktop.getDockables().length > 1) {

                desktop.split(dock, boosterTableView, DockingConstants.SPLIT_BOTTOM);
                desktop.setDockableHeight(boosterTableView, 0.2d);
            }
            else {
                desktop.split(dock, boosterTableView, DockingConstants.SPLIT_RIGHT);
            }
        }
        else {
            desktop.addDockable(boosterTableView, RelativeDockablePosition.RIGHT);
        }

        // create the nodeList listener
        final NodeListListener nodeListListener = new DefaultNodeListListener() {

            @Override
            public void listNodeAdded(final NodeInterface node) {
                LOGGER.info("The nodelist has a new node: {}", node);

                nodeNew(node);
            }

            @Override
            public void listNodeRemoved(final NodeInterface node) {
                LOGGER.info("The nodelist has a node removed: {}", node);
                nodeLost(node);
            }
        };
        // register as nodeList listener at the main model
        this.nodeListProvider.addNodeListListener(nodeListListener);

        try {
            connectionService.subscribeConnectionStatusChanges(connectionInfo -> {

                if (connectionInfo.getConnectionId().equals(ConnectionRegistry.CONNECTION_ID_MAIN)) {
                    LOGGER.info("Current state: {}", connectionInfo.getConnectionState());

                    switch (connectionInfo.getConnectionState().getActualPhase()) {
                        case CONNECTED:
                            LOGGER.info("The communication was opened.");
                            break;
                        case DISCONNECTED:
                            LOGGER.info("The communication was closed.");

                            List boosters = new LinkedList<>(boosterTableModel.getBoosters());
                            for (BoosterModel booster : boosters) {
                                SwingUtils.executeInEDT(() -> boosterTableModel.removeBooster(booster.getBooster()));
                            }
                            break;
                        default:
                            break;
                    }
                }

            }, error -> {
                LOGGER.warn("The connection status change caused an error.", error);
            });

            final NodeProvider nodeProvider = this.nodeProviderSupplier.get();
            if (nodeProvider != null) {
                Collection nodes = nodeProvider.getNodes();
                if (CollectionUtils.isNotEmpty(nodes)) {
                    for (NodeInterface node : nodes) {
                        LOGGER.info("Initially add node.");
                        nodeNew(node);
                    }
                }
            }
        }
        catch (Exception ex) {
            LOGGER.warn("Register controller as connection status listener failed.", ex);
        }

        this.dockableStateChangeListener = new DockableStateChangeListener() {

            @Override
            public void dockableStateChanged(DockableStateChangeEvent event) {
                if (event.getNewState().getDockable().equals(boosterTableView) && event.getNewState().isClosed()) {
                    LOGGER.info("BoosterTableView was closed, free resources.");

                    try {
                        desktop.removeDockableStateChangeListener(dockableStateChangeListener);
                    }
                    catch (Exception ex) {
                        LOGGER
                            .warn("Remove dockableStateChangeListener from desktop failed: "
                                + dockableStateChangeListener, ex);
                    }
                    finally {
                        dockableStateChangeListener = null;
                    }

                    try {
                        // remove node listener from communication factory
                        if (nodeListListener != null) {
                            BoosterTableController.this.nodeListProvider.removeNodeListListener(nodeListListener);
                        }
                    }
                    catch (Exception ex) {
                        LOGGER.warn("Unregister controller as node listener failed.", ex);
                    }

                    // stop the booster current timer
                    if (boosterCurrentTimer != null) {
                        LOGGER.info("Stop the booster current timer.");
                        boosterCurrentTimer.stop();
                        boosterCurrentTimer = null;
                    }

                    // release the view instance
                    boosterTableView = null;
                }
            }
        };
        desktop.addDockableStateChangeListener(this.dockableStateChangeListener);

        try {
            // start the booster current timer
            boosterCurrentTimer = new Timer(1000, new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    LOGGER.trace("The booster current timer has elapsed.");

                    long now = System.currentTimeMillis();

                    try {

                        // trigger the model
                        List boosters = boosterTableModel.getBoosters();
                        for (BoosterModel boosterModel : boosters) {
                            Integer current = boosterModel.getCurrent();
                            if (current != null && current > 0
                                && boosterModel.getLastCurrentUpdate() < (now - CURRENT_UPDATE_TIMEOUT)) {
                                // the current value is outdated -> clear the value
                                LOGGER
                                    .info("the current value is outdated -> clear the value, booster: {}",
                                        boosterModel);

                                boosterTableModel.setBoosterCurrent(boosterModel.getBooster(), null, null);
                            }
                        }
                    }
                    catch (Exception ex) {
                        LOGGER.warn("Clear the outdated booster current value failed.", ex);
                    }
                }
            });
            boosterCurrentTimer.setCoalesce(true);
            boosterCurrentTimer.start();
        }
        catch (Exception ex) {
            LOGGER.warn("Start the booster current timer failed.", ex);
        }

        return boosterTableView;
    }

    private void nodeLost(final NodeInterface node) {
        if (NodeUtils.hasBoosterFunctions(node.getUniqueId())
            || NodeUtils.hasCommandStationFunctions(node.getUniqueId())) {
            LOGGER.info("Remove booster from model: {}", node);
            SwingUtils.executeInEDT(() -> boosterTableModel.removeBooster(node));
        }
    }

    private void nodeNew(final NodeInterface node) {
        if (NodeUtils.hasBoosterFunctions(node.getUniqueId())
            || NodeUtils.hasCommandStationFunctions(node.getUniqueId())) {

            LOGGER.info("New booster in system detected: {}", node);
            SwingUtils.executeInEDT(() -> internalNewNode(node));
        }
    }

    private void internalNewNode(final NodeInterface node) {
        LOGGER.info("Add new booster to table: {}", node);

        boosterTableModel.addBooster(node, this.wizardLabelWrapper);

        // if the booster is not loaded we do not query the max current
        if (StatusIdentifier.InitialLoadPending == node.getNodeLoadStatusIdentifier()) {
            LOGGER
                .info("The initial load of the booster node is still pending. Skip fetch booster state for node: {}",
                    node);
            return;
        }

        fetchBoosterState(node);
    }

    private void fetchBoosterState(final NodeInterface node) {
        LOGGER.info("Fetch the booster state.");

        // get the command station state
        try {
            if (NodeUtils.hasBoosterFunctions(node.getUniqueId())) {

                // trigger fetch the max current value
                boosterTableModel.triggerFetchBoosterMaxCurrent(node);

                // trigger the booster state
                serviceWorker
                    .submit(() -> boosterService
                        .queryBoosterState(ConnectionRegistry.CONNECTION_ID_MAIN, node.getBoosterNode()));
            }

            if (NodeUtils.hasCommandStationFunctions(node.getUniqueId()) && node.getCommandStationNode() != null) {
                // trigger the command station state
                serviceWorker
                    .submit(() -> commandStationService
                        .queryCommandStationState(ConnectionRegistry.CONNECTION_ID_MAIN, node.getCommandStationNode()));
            }
        }
        catch (Exception ex) {
            LOGGER.warn("Get the booster and command station state failed.", ex);

            // TODO set an error flag or something in the node
        }
    }

    @Override
    public void setBoosterState(final NodeInterface node, final BoosterStatus boosterStatus) {
        LOGGER.info("Set the booster status, node: {}, boosterStatus: {}", node, boosterStatus);
        try {
            if (node.getBoosterNode() != null) {
                this.boosterService
                    .setBoosterState(ConnectionRegistry.CONNECTION_ID_MAIN, node.getBoosterNode(), boosterStatus);
            }
            else {
                this.boosterService
                    .setBoosterState(ConnectionRegistry.CONNECTION_ID_MAIN, node.getInterfaceNode(), boosterStatus);
            }
        }
        catch (Exception ex) {
            LOGGER.warn("Set the booster status failed.", ex);

            // TODO set an error flag or something in the node
        }
    }

    @Override
    public void setCommandStationState(NodeInterface node, CommandStationState csStatus) {
        LOGGER.info("Set the command station status, node: {}, csStatus: {}", node, csStatus);
        try {
            this.commandStationService
                .setCommandStationState(ConnectionRegistry.CONNECTION_ID_MAIN, node.getCommandStationNode(),
                    CommandStationStatus.valueOf(csStatus));
        }
        catch (Exception ex) {
            LOGGER.warn("Set the command station status failed.", ex);

            // TODO set an error flag or something in the node
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy