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

org.bidib.wizard.mvc.position.controller.FeedbackPositionController Maven / Gradle / Ivy

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

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;

import javax.annotation.PostConstruct;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;

import org.bidib.jbidibc.core.schema.bidibbase.BaseLabel;
import org.bidib.jbidibc.core.schema.bidiblabels.NodeLabels;
import org.bidib.jbidibc.messages.enums.LcOutputType;
import org.bidib.jbidibc.messages.enums.PortConfigKeys;
import org.bidib.jbidibc.messages.utils.ProductUtils;
import org.bidib.wizard.api.locale.Resources;
import org.bidib.wizard.api.model.NodeInterface;
import org.bidib.wizard.api.model.NodeProvider;
import org.bidib.wizard.api.model.PositionAddressData;
import org.bidib.wizard.api.model.PositionFeedbackPort;
import org.bidib.wizard.api.model.RfBasisNode;
import org.bidib.wizard.api.model.connection.AbstractMessageEvent;
import org.bidib.wizard.api.model.connection.BidibConnection;
import org.bidib.wizard.api.model.connection.event.OccupancyPositionMessageEvent;
import org.bidib.wizard.api.model.listener.DefaultNodeListListener;
import org.bidib.wizard.api.model.listener.NodeListListener;
import org.bidib.wizard.client.common.model.listener.PortModelListener;
import org.bidib.wizard.client.common.view.DockKeys;
import org.bidib.wizard.client.common.view.DockUtils;
import org.bidib.wizard.common.exception.ConnectionException;
import org.bidib.wizard.common.labels.BidibLabelUtils;
import org.bidib.wizard.common.labels.LabelsChangedEvent;
import org.bidib.wizard.common.labels.WizardLabelFactory;
import org.bidib.wizard.common.labels.WizardLabelWrapper;
import org.bidib.wizard.core.model.connection.ConnectionRegistry;
import org.bidib.wizard.core.model.connection.MessageEventHandler;
import org.bidib.wizard.core.service.ConnectionService;
import org.bidib.wizard.mvc.main.controller.MainControllerInterface;
import org.bidib.wizard.mvc.main.view.exchange.NodeExchangeHelper;
import org.bidib.wizard.mvc.position.model.FeedbackPositionModel;
import org.bidib.wizard.mvc.position.model.FeedbackPositionTableModel;
import org.bidib.wizard.mvc.position.model.listener.DefaultFeedbackPositionListener;
import org.bidib.wizard.mvc.position.model.listener.FeedbackPositionListener;
import org.bidib.wizard.mvc.position.view.FeedbackPositionView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;

import com.vlsolutions.swing.docking.Dockable;
import com.vlsolutions.swing.docking.DockingDesktop;
import com.vlsolutions.swing.docking.RelativeDockablePosition;
import com.vlsolutions.swing.docking.event.DockableStateChangeEvent;
import com.vlsolutions.swing.docking.event.DockableStateChangeListener;

import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;

public class FeedbackPositionController {
    private static final Logger LOGGER = LoggerFactory.getLogger(FeedbackPositionController.class);

    private FeedbackPositionView feedbackPositionView;

    private FeedbackPositionModel feedbackPositionModel;

    private FeedbackPositionListener feedbackPositionListener;

    @Autowired
    private ConnectionService connectionService;

    @Autowired
    private WizardLabelWrapper wizardLabelWrapper;

    private final DockingDesktop desktop;

    private DockableStateChangeListener dockableStateChangeListener;

    private final MainControllerInterface mainController;

    private final Supplier nodeProviderSupplier;

    private CompositeDisposable compDispMessages;

    public FeedbackPositionController(final DockingDesktop desktop, final Supplier nodeProviderSupplier,
        final MainControllerInterface mainController) {
        LOGGER.info("Create new instance of FeedbackPositionController.");

        this.desktop = desktop;
        this.nodeProviderSupplier = nodeProviderSupplier;
        this.mainController = mainController;
    }

    @PostConstruct
    public void initialize() {

        feedbackPositionModel = new FeedbackPositionModel((positionId) -> {
            final NodeLabels nodeLabels = getNodeLabels();
            BaseLabel label = BidibLabelUtils.getFeedbackPositionLabel(nodeLabels, positionId);
            return label;
        });

        prepareMessageMap();

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

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

                nodeNew(node);
            }

            @Override
            public void listNodeRemoved(NodeInterface node) {
                LOGGER.info("The nodelist has a node removed: {}", node);
                nodeLost(node);
            }
        };
        // register as nodeList listener at the main controller
        mainController.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.");
                            this.compDispMessages = new CompositeDisposable();

                            registerForMessages();

                            break;
                        case DISCONNECTED:
                            LOGGER.info("The communication was closed.");

                            if (compDispMessages != null) {
                                compDispMessages.dispose();
                                compDispMessages = null;
                            }
                            break;
                        default:
                            break;
                    }
                }

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

        }
        catch (Exception ex) {
            LOGGER.warn("Register controller as connection status listener failed.", ex);
        }

        try {
            LOGGER.info("Check if the connection is connected already.");
            boolean isConnected = connectionService.isConnected(ConnectionRegistry.CONNECTION_ID_MAIN);
            if (isConnected) {
                LOGGER.info("The connection is connected. Register for messages.");
                if (this.compDispMessages == null) {
                    this.compDispMessages = new CompositeDisposable();
                }
                registerForMessages();
            }
        }
        catch (Exception ex) {
            LOGGER.warn("Register for messages failed.", ex);
        }

        final NodeProvider nodeProvider = this.nodeProviderSupplier.get();
        if (nodeProvider != null) {
            // initialize the node list
            for (NodeInterface node : nodeProvider.getNodes()) {
                nodeNew(node);
            }
        }
    }

    public void start() {

        // check if the booster table view is already opened
        String searchKey = DockKeys.FEEDBACK_POSITION_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;
        }

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

        final FeedbackPositionTableModel tableModel = new FeedbackPositionTableModel();

        feedbackPositionView = new FeedbackPositionView(this, feedbackPositionModel, tableModel);

        // add the feedback position panel to the right of the node tree
        if (desktop.getDockables().length > 1) {
            Dockable dock = desktop.getDockables()[1].getDockable();

            desktop.createTab(dock, feedbackPositionView, 1, true);
        }
        else {
            desktop.addDockable(feedbackPositionView, RelativeDockablePosition.RIGHT);
        }

        // add the feedback port listener
        feedbackPositionListener = new DefaultFeedbackPositionListener() {
            @Override
            public void addressesChanged(PositionFeedbackPort port, Collection addresses) {
                LOGGER.info("The addresses have changed, port: {}, addresses: {}", port, addresses);
                // feedbackPortTableModel.updatePort(port);
            }

            @Override
            public void labelChanged(PositionFeedbackPort port, String label) {

                LOGGER.info("The label has changed, port: {}, label: {}", port, label);

                // TODO this is never called

                port.setLabel(label);

                final NodeInterface node = getSelectedNode();
                if (node != null) {
                    // update the stored labels in the single base or the master base
                    try {
                        NodeLabels nodeLabels = getNodeLabels();
                        BidibLabelUtils.replaceFeedbackPositionLabel(nodeLabels, port.getId(), port.getLabel());
                        saveLabels();
                    }
                    catch (Exception ex) {
                        LOGGER.warn("Save feedback position labels failed.", ex);

                        String labelPath = ex.getMessage();
                        JOptionPane
                            .showMessageDialog(JOptionPane.getFrameForComponent(null),
                                Resources
                                    .getString(NodeExchangeHelper.class, "labelfileerror.message",
                                        new Object[] { labelPath }),
                                Resources.getString(NodeExchangeHelper.class, "labelfileerror.title"),
                                JOptionPane.ERROR_MESSAGE);

                        throw new RuntimeException(ex);
                    }
                }
                else {
                    LOGGER.warn("No node selected, labels are not stored.");
                }
            }
        };
        this.feedbackPositionModel.addFeedbackPortListener(feedbackPositionListener);

        tableModel.setPortListener(new PortModelListener() {

            @Override
            public void labelChanged(PositionFeedbackPort port, String label) {

                LOGGER.info("The label has changed, port: {}, label: {}", port, label);

                final NodeInterface node = getSelectedNode();
                if (node != null) {
                    // update the stored labels in the single base or the master base
                    try {
                        NodeLabels nodeLabels = getNodeLabels();
                        BidibLabelUtils.replaceFeedbackPositionLabel(nodeLabels, port.getId(), port.getLabel());
                        saveLabels();
                    }
                    catch (Exception ex) {
                        LOGGER.warn("Save feedback position labels failed.", ex);

                        String labelPath = ex.getMessage();
                        JOptionPane
                            .showMessageDialog(JOptionPane.getFrameForComponent(null),
                                Resources
                                    .getString(NodeExchangeHelper.class, "labelfileerror.message",
                                        new Object[] { labelPath }),
                                Resources.getString(NodeExchangeHelper.class, "labelfileerror.title"),
                                JOptionPane.ERROR_MESSAGE);

                        throw new RuntimeException(ex);
                    }
                }
                else {
                    LOGGER.warn("No node selected, labels are not stored.");
                }
            }

            @Override
            public void configChanged(PositionFeedbackPort port, PortConfigKeys... portConfigKeys) {
            }

            @Override
            public void changePortType(LcOutputType portType, PositionFeedbackPort port) {
            }
        });

        this.dockableStateChangeListener = new DockableStateChangeListener() {

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

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

                    try {
                        feedbackPositionView.cleanup();
                    }
                    catch (Exception ex) {
                        LOGGER.warn("Unregister controller as node listener failed.", ex);
                    }
                }
            }
        };
        desktop.addDockableStateChangeListener(this.dockableStateChangeListener);
    }

    private void registerForMessages() {
        LOGGER.info("Register for messages from the connection.");
        // register for messages from the connection
        try {
            BidibConnection connection = connectionService.find(ConnectionRegistry.CONNECTION_ID_MAIN);
            Disposable dispMessages = connection.getSubjectMessages().subscribe(msg -> {
                handleBidibMessageEvent(msg);
            });

            compDispMessages.add(dispMessages);
        }
        catch (ConnectionException ex) {
            LOGGER.warn("No connection found, register on messages is skipped.", ex);
        }
    }

    private final Map, MessageEventHandler> messageActionMap =
        new HashMap<>();

    private void prepareMessageMap() {
        LOGGER.info("Prepare the message map.");

        // add message events for occupancy position
        messageActionMap.put(OccupancyPositionMessageEvent.class, evt -> {
            OccupancyPositionMessageEvent event = (OccupancyPositionMessageEvent) evt;

            LOGGER.info("Process the event: {}", event);

            switch (event.getLocationType()) {
                case LOCATOR_BADGE:
                    SwingUtilities.invokeLater(() -> {
                        LOGGER.info("Update the position: {}", event);
                        feedbackPositionModel
                            .addPosition(event.getAddress(), event.getDecoderAddress(), event.getLocationType(),
                                event.getLocationAddress(), null);
                    });
                    break;
                case RFID_READOUT:
                case AXLE_COUNTER:
                    SwingUtilities.invokeLater(() -> {
                        LOGGER.info("Update the position: {}", event);
                        feedbackPositionModel
                            .addPosition(event.getAddress(), event.getDecoderAddress(), event.getLocationType(),
                                event.getLocationAddress(), event.getExtendedData());
                    });
                    break;
                default:
                    LOGGER.debug("Do not show position feedback with type: {}", event.getLocationType());
                    break;
            }
        });
    }

    private void handleBidibMessageEvent(AbstractMessageEvent event) {
        LOGGER.debug("Handle the message event: {}", event);

        // let the action update the cached value in the node
        try {
            MessageEventHandler action = messageActionMap.get(event.getClass());
            if (action != null) {
                action.handleMessageEvent(event);
            }
            else {
                LOGGER.debug("No message event action configured for event: {}", event);
            }
        }
        catch (Exception ex) {
            LOGGER.warn("Execute the message event action failed, event: {}", event, ex);
        }
    }

    protected void nodeLost(final NodeInterface node) {

    }

    private final Object rfBasisNodesLock = new Object();

    protected void nodeNew(final NodeInterface node) {
        if (ProductUtils.isRFBasisNode(node.getUniqueId())) {

            synchronized (rfBasisNodesLock) {
                LOGGER.info("Register new RF-Basis Node: {}", node);

                RfBasisNode rfBasisNode = new RfBasisNode(node);
                feedbackPositionModel.addRfBasisNode(rfBasisNode);
            }
        }
    }

    /**
     * @return the selected node
     */
    public NodeInterface getSelectedNode() {

        synchronized (rfBasisNodesLock) {
            RfBasisNode rfBasisNode =
                // always return the single base or the master base
                feedbackPositionModel
                    .getRfBasisNodes().stream()
                    .filter(rf -> (rf.getNode().getBaseNumber() == 0 || rf.getNode().getBaseNumber() == 1)).findFirst()
                    .orElse(null);

            if (rfBasisNode != null) {
                return rfBasisNode.getNode();
            }

        }
        return null;
    }

    private NodeLabels getNodeLabels() {
        final WizardLabelFactory wizardLabelFactory = wizardLabelWrapper.getWizardLabelFactory();

        NodeLabels nodeLabels = wizardLabelFactory.loadLabels(getSelectedNode().getUniqueId());
        return nodeLabels;
    }

    private void saveLabels() {
        try {
            long uniqueId = getSelectedNode().getUniqueId();
            wizardLabelWrapper.saveNodeLabels(uniqueId);
        }
        catch (Exception e) {
            LOGGER.warn("Save accessory labels failed.", e);
            throw new RuntimeException(e);
        }
    }

    @EventListener(LabelsChangedEvent.class)
    public void labelsChangedEvent(LabelsChangedEvent labelsChangedEvent) {
        LOGGER.info("The labels have changed, node: {}", labelsChangedEvent);

        if (this.feedbackPositionView != null) {
            SwingUtilities.invokeLater(() -> this.feedbackPositionView.refreshView());
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy