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

org.bidib.wizard.mvc.pomupdate.controller.PomUpdateController Maven / Gradle / Ivy

There is a newer version: 2.0.0-M1
Show newest version
package org.bidib.wizard.mvc.pomupdate.controller;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;

import org.bidib.jbidibc.core.AddressData;
import org.bidib.jbidibc.core.BidibLibrary;
import org.bidib.jbidibc.core.DefaultMessageListener;
import org.bidib.jbidibc.core.Feature;
import org.bidib.jbidibc.core.MessageListener;
import org.bidib.jbidibc.core.enumeration.AddressTypeEnum;
import org.bidib.jbidibc.core.enumeration.CommandStationPom;
import org.bidib.jbidibc.core.enumeration.PomAcknowledge;
import org.bidib.jbidibc.core.message.CommandStationPomMessage;
import org.bidib.jbidibc.core.utils.ByteUtils;
import org.bidib.wizard.comm.Communication;
import org.bidib.wizard.comm.CommunicationFactory;
import org.bidib.wizard.locale.Resources;
import org.bidib.wizard.mvc.common.view.DockKeys;
import org.bidib.wizard.mvc.main.controller.MainControllerInterface;
import org.bidib.wizard.mvc.main.model.MainModel;
import org.bidib.wizard.mvc.main.model.Node;
import org.bidib.wizard.mvc.main.model.listener.NodeListListener;
import org.bidib.wizard.mvc.pomupdate.model.Decoder;
import org.bidib.wizard.mvc.pomupdate.model.PomUpdateModel;
import org.bidib.wizard.mvc.pomupdate.view.PomUpdateView;
import org.bidib.wizard.mvc.pomupdate.view.listener.DecoderInfoStatusListener;
import org.bidib.wizard.mvc.pomupdate.view.listener.PomUpdatePerformStatusListener;
import org.bidib.wizard.mvc.pomupdate.view.listener.PomUpdateStatusListener;
import org.bidib.wizard.mvc.pomupdate.view.listener.PomUpdateViewListener;
import org.bidib.wizard.mvc.preferences.model.Preferences;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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;

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

    private static final int MIN_POM_REPEAT = 6;

    private final DockingDesktop desktop;

    private final Node node;

    private final MainModel mainModel;

    private PomUpdateModel pomUpdateModel;

    private PomUpdateView pomUpdateView;

    private MessageListener messageListener;

    private MainControllerInterface mainController;

    private Node commandStationNode;

    private int pomRepeat = 1;

    protected final ScheduledExecutorService updateWorker = Executors.newScheduledThreadPool(1);

    public PomUpdateController(final MainModel mainModel, final DockingDesktop desktop, final Node node) {
        this.mainModel = mainModel;
        this.desktop = desktop;
        this.node = node;
    }

    public void start(final MainControllerInterface mainController) {
        this.mainController = mainController;

        // check if the pom update view is already opened
        String searchKey = DockKeys.POM_UPDATE_VIEW;
        LOGGER.info("Search for view with key: {}", searchKey);
        Dockable view = desktop.getContext().getDockableByKey(searchKey);
        if (view != null) {
            LOGGER.info("Select the existing pom update view instead of open a new one.");
            selectWindow(view);
            return;
        }

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

        pomUpdateModel = new PomUpdateModel();
        pomUpdateView = new PomUpdateView(desktop, pomUpdateModel);

        DockableState[] dockables = desktop.getDockables();
        LOGGER.info("Current dockables: {}", new Object[] { dockables });
        if (dockables.length > 1) {

            DockableState tabPanelNodeDetails = null;
            // search the node details tab panel
            for (DockableState dockable : dockables) {

                if (DockKeys.DOCKKEY_TAB_PANEL.equals(dockable.getDockable().getDockKey())) {
                    LOGGER.info("Found the tab panel dockable.");
                    tabPanelNodeDetails = dockable;

                    break;
                }
            }

            Dockable dock = desktop.getDockables()[1].getDockable();
            if (tabPanelNodeDetails != null) {
                LOGGER.info("Add the pom update view next to the node details panel.");
                dock = tabPanelNodeDetails.getDockable();

                TabbedDockableContainer container = DockingUtilities.findTabbedDockableContainer(dock);
                int order = 0;
                if (container != null) {
                    order = container.getTabCount();
                }
                LOGGER.info("Add new pomUpdateView at order: {}", order);

                desktop.createTab(dock, pomUpdateView, order, true);
            }
            else {
                desktop.split(dock, pomUpdateView, DockingConstants.SPLIT_RIGHT);
            }
        }
        else {
            desktop.addDockable(pomUpdateView, RelativeDockablePosition.RIGHT);
        }

        final Communication communication = CommunicationFactory.getInstance();

        pomUpdateView.addPomUpdateViewListener(new PomUpdateViewListener() {

            @Override
            public void close() {
                if (messageListener != null) {
                    LOGGER.info("Remove the message listener.");
                    communication.removeMessageListener(messageListener);

                    messageListener = null;
                }
                // fireClose();
            }

            @Override
            public void prepareUpdate(
                final Map> prepareUpdateMap,
                final PomUpdateStatusListener statusListener) {
                LOGGER.info("Prepare the POM update for decoders.");

                final Node commandStationNode = getCommandStationNode(communication);
                if (commandStationNode == null) {
                    LOGGER.warn("No command station node available! Operation aborted!");

                    // show dialog
                    JOptionPane.showMessageDialog(null,
                        Resources.getString(PomUpdateController.class, "no_commandstation_available"));
                    return;
                }

                Runnable runnable = new Runnable() {
                    public void run() {
                        for (Entry> entry : prepareUpdateMap.entrySet()) {
                            Decoder decoder = entry.getKey();
                            LOGGER.debug("Reset the update prepare status for decoder: {}", decoder);
                            int progress = 0;
                            statusListener.updateStatus(decoder, progress);
                        }

                        for (Entry> entry : prepareUpdateMap.entrySet()) {
                            Decoder decoder = entry.getKey();
                            LOGGER.info("Prepare the decoder: {}", decoder);

                            int progress = 0;
                            statusListener.updateStatus(decoder, progress);

                            List pomMessages = entry.getValue();
                            for (CommandStationPomMessage pomMessage : pomMessages) {

                                PomAcknowledge ack =
                                    communication.sendCvPomRequest(commandStationNode.getNode(),
                                        pomMessage.getDecoderAddress(),
                                        CommandStationPom.valueOf(ByteUtils.getLowByte(pomMessage.getOpCode())),
                                        pomMessage.getCvNumber(), pomMessage.getCvValue());
                                LOGGER.info("Send POM was acknowledgded: {}", ack);

                                progress += 8;
                                statusListener.updateStatus(decoder, progress);
                            }

                            progress = 100;
                            statusListener.updateStatus(decoder, progress);
                        }
                    }
                };

                // Start the prepare update process
                updateWorker.execute(runnable);
            }

            @Override
            public void performUpdate(
                final List updateMessages, final PomUpdatePerformStatusListener statusListener) {

                statusListener.updateStatus(0);

                LOGGER.info("Perform the POM update for decoders.");
                final Node commandStationNode = getCommandStationNode(communication);
                if (commandStationNode == null) {
                    LOGGER.warn("No command station node available! Operation aborted!");

                    // show dialog
                    JOptionPane.showMessageDialog(null,
                        Resources.getString(PomUpdateController.class, "no_commandstation_available"));
                    return;
                }

                final int initialDelay = Preferences.getInstance().getPomUpdateInitialDelay();
                final int delayBetweenPackets = Preferences.getInstance().getPomUpdateDelayBetweenPackets();
                // int resendPacketCount = Preferences.getInstance().getPomUpdateResendPacketCount();

                // calculate how often the POM must be sent to make sure 5-6 repeats are sent
                final int resendPomPacketCount = PomUpdateUtils.calculateResendPacketCount(pomRepeat, MIN_POM_REPEAT);

                LOGGER.info(
                    "POM update, initialDelay: {}, delayBetweenPackets: {}, resendPacketCount: {}, pomRepeat: {}",
                    initialDelay, delayBetweenPackets, resendPomPacketCount, pomRepeat);

                Runnable runnable = new Runnable() {
                    public void run() {
                        final int totalPackets = updateMessages.size();
                        LOGGER.info("Start sending update packets: {}", totalPackets);
                        int progress = 0;
                        int currentPacket = 0;

                        for (CommandStationPomMessage pomMessage : updateMessages) {

                            for (int currentResendCounter = 0; currentResendCounter < resendPomPacketCount; currentResendCounter++) {
                                PomAcknowledge ack =
                                    communication.sendCvPomRequest(commandStationNode.getNode(),
                                        pomMessage.getDecoderAddress(),
                                        CommandStationPom.valueOf(ByteUtils.getLowByte(pomMessage.getOpCode())),
                                        pomMessage.getCvNumber(), pomMessage.getCvValue());
                                LOGGER.info("Send POM was acknowledgded: {}, currentResendCounter: {}", ack,
                                    currentResendCounter);
                            }

                            currentPacket++;

                            progress = (currentPacket * 100) / totalPackets;

                            statusListener.updateStatus(progress);

                            // wait some time after send packet
                            if (currentPacket == 1) {
                                LOGGER.info("Wait {}ms after send initiating packet.", initialDelay);
                                try {
                                    Thread.sleep(initialDelay);
                                }
                                catch (InterruptedException ex) {
                                    LOGGER.warn("Wait {}ms after send initiating packet failed.", initialDelay, ex);
                                }
                            }
                            else {
                                LOGGER.info("Wait {}ms after send pom packet.", delayBetweenPackets);
                                try {
                                    Thread.sleep(delayBetweenPackets);
                                }
                                catch (InterruptedException ex) {
                                    LOGGER.warn("Wait {}ms after send pom packet failed.", delayBetweenPackets, ex);
                                }
                            }

                        }

                        statusListener.finished();
                    }
                };

                // Start the prepare update process
                updateWorker.execute(runnable);
            }

            @Override
            public void performLoadDecoderInfo(
                final List decodersToLoadInfo, final DecoderInfoStatusListener statusListener) {
                LOGGER.info("Load decoder info for decoders: {}", decodersToLoadInfo);

                statusListener.updateStatus(0);

                final Node commandStationNode = getCommandStationNode(communication);
                if (commandStationNode == null) {
                    LOGGER.warn("No command station node available! Operation aborted!");

                    // show dialog
                    JOptionPane.showMessageDialog(null,
                        Resources.getString(PomUpdateController.class, "no_commandstation_available"));
                    return;
                }

                Runnable runnable = new Runnable() {
                    public void run() {
                        final int totalDecoders = decodersToLoadInfo.size();
                        LOGGER.info("Start get decoder info for total decoder count: {}", totalDecoders);
                        int progress = 0;
                        int currentDecoder = 0;
                        for (Decoder decoder : decodersToLoadInfo) {
                            // fetch CV 7 and CV 8 for every decoder
                            AddressData addressData =
                                new AddressData(decoder.getAddress(), AddressTypeEnum.LOCOMOTIVE_FORWARD);

                            PomAcknowledge ack =
                                communication.sendReadCvPomRequest(commandStationNode.getNode(), addressData,
                                    CommandStationPom.RD_BYTE, 7 /* CV 7 : decoder version */);
                            LOGGER.info("Send read POM CV 7 was acknowledgded: {}", ack);

                            ack =
                                communication.sendReadCvPomRequest(commandStationNode.getNode(), addressData,
                                    CommandStationPom.RD_BYTE, 8 /* CV 8 : manufacturer */);
                            LOGGER.info("Send read POM CV 8 was acknowledgded: {}", ack);

                            currentDecoder++;

                            progress = (currentDecoder * 100) / totalDecoders;

                            statusListener.updateStatus(progress);
                        }
                        LOGGER.info("Send POM messages to get the decoder info has finished. Wait for the answers.");
                        statusListener.finished();
                    }
                };

                // Start the prepare update process
                updateWorker.execute(runnable);

            }
        });

        // add the node list listener
        mainModel.addNodeListListener(this);

        messageListener = new DefaultMessageListener() {
            @Override
            public void feedbackCv(byte[] address, final AddressData decoderAddress, int cvNumber, int cvData) {
                LOGGER.info("CV was received, node addr: {}, decoder address: {}, cvNumber: {}, cvData: {}", address,
                    decoderAddress, cvNumber, cvData);

                updatePomFeedback(decoderAddress, cvNumber, cvData);

                // special processing for Tams decoder
                if (cvNumber == 8 && cvData == 62) {
                    // Tams is manufaturer
                    LOGGER.info("Prepare to get the product id for the Tams decoder from CV 803.");

                    Runnable runnable = new Runnable() {
                        public void run() {
                            LOGGER.info("Get the product id for the Tams decoder from CV 803.");
                            final Node commandStationNode = getCommandStationNode(communication);
                            if (commandStationNode == null) {
                                LOGGER.warn("No command station node available! Operation aborted!");
                            }
                            else {
                                PomAcknowledge ack =
                                    communication.sendReadCvPomRequest(commandStationNode.getNode(), decoderAddress,
                                        CommandStationPom.RD_BYTE, 803 /* CV 803 : decoder identification */);
                                LOGGER.info("Send read POM CV 803 was acknowledgded: {}", ack);
                            }
                        }
                    };

                    // Start the prepare update process
                    updateWorker.execute(runnable);

                }
            }
        };

        communication.addMessageListener(messageListener);
    }

    private Node getCommandStationNode(final Communication communication) {
        // final Communication communication = CommunicationFactory.getInstance();
        if (commandStationNode == null) {
            // search the command station node
            Collection nodes = mainController.getNodes();

            for (Node node : nodes) {
                if (node.isCommandStation()) {
                    commandStationNode = node;
                    LOGGER.info("Found a command station node: {}", commandStationNode);

                    Feature genPomRepeat =
                        communication.readFeature(node.getNode(), BidibLibrary.FEATURE_GEN_POM_REPEAT, true);
                    if (genPomRepeat != null) {
                        pomRepeat = genPomRepeat.getValue();
                        LOGGER.info("Fetched pomRepeat count from command station node: ", pomRepeat);
                    }
                    else {
                        LOGGER.info("The command station node does not provide the feature FEATURE_GEN_POM_REPEAT.");
                        pomRepeat = 1;
                    }
                    break;
                }
            }
        }
        return commandStationNode;
    }

    @Override
    public void nodeChanged() {
        LOGGER.debug("The node has changed, current node in model: {}", node);
        if (SwingUtilities.isEventDispatchThread()) {
            internalNodeChanged();
        }
        else {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    internalNodeChanged();
                }
            });
        }
    }

    private void internalNodeChanged() {

        LOGGER.debug("handle node has changed, node: {}", node);

        if (node != null && node.equals(mainModel.getSelectedNode())) {
            LOGGER.debug("The node in the model has not changed.");
            return;
        }

        // check if we must close the pomUpdate view
        if (pomUpdateView != null) {
            LOGGER.info("Close the pomUpdate view: {}", pomUpdateView);

            desktop.close(pomUpdateView);
        }

        // unregister node list listener
        mainModel.removeNodeListListener(this);
    }

    @Override
    public void listChanged() {
        // TODO Auto-generated method stub

    }

    @Override
    public void nodeStateChanged() {
    }

    @Override
    public void listNodeAdded(Node node) {
    }

    @Override
    public void listNodeRemoved(Node node) {
        // TODO Auto-generated method stub

    }

    private void selectWindow(Dockable dockable) {

        TabbedDockableContainer container = DockingUtilities.findTabbedDockableContainer(dockable);
        if (container != null) {
            container.setSelectedDockable(dockable);
        }
        else {
            LOGGER.warn("Container not available, select component directly.");
            dockable.getComponent().requestFocusInWindow();
        }
    }

    private void updatePomFeedback(AddressData decoderAddress, int cvNumber, int cvData) {

        pomUpdateModel.updatePomFeedback(decoderAddress, cvNumber, cvData);

    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy