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

org.bidib.wizard.mvc.stepcontrol.view.StepControlPanel Maven / Gradle / Ivy

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

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.net.URL;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Collections;
import java.util.Comparator;
import java.util.EventObject;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListModel;
import javax.swing.RowSorter;
import javax.swing.SortOrder;
import javax.swing.SwingUtilities;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableModel;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.bidib.jbidibc.core.AccessoryState;
import org.bidib.jbidibc.core.node.ConfigurationVariable;
import org.bidib.jbidibc.core.utils.AccessoryStateUtils.ErrorAccessoryState.AccessoryExecutionState;
import org.bidib.jbidibc.core.utils.ByteUtils;
import org.bidib.jbidibc.core.utils.ProductUtils;
import org.bidib.wizard.comm.FeedbackPortStatus;
import org.bidib.wizard.locale.Resources;
import org.bidib.wizard.main.DefaultApplicationContext;
import org.bidib.wizard.mvc.common.view.converter.StringToUnsignedLongConverter;
import org.bidib.wizard.mvc.common.view.converter.TurnTableTypeConverter;
import org.bidib.wizard.mvc.common.view.cvdefinition.CvDefinitionTreeTableModel;
import org.bidib.wizard.mvc.common.view.cvdefinition.CvDefintionPanelProvider;
import org.bidib.wizard.mvc.common.view.cvdefinition.CvValueUtils;
import org.bidib.wizard.mvc.common.view.table.ButtonColumn;
import org.bidib.wizard.mvc.common.view.table.CustomBooleanCellEditor;
import org.bidib.wizard.mvc.common.view.table.CustomBooleanCellRenderer;
import org.bidib.wizard.mvc.common.view.table.LineNumberTableRowHeader;
import org.bidib.wizard.mvc.main.model.MainModel;
import org.bidib.wizard.mvc.main.model.Node;
import org.bidib.wizard.mvc.main.model.Port;
import org.bidib.wizard.mvc.main.model.listener.CvDefinitionRequestListener;
import org.bidib.wizard.mvc.main.model.listener.DefaultFeedbackPortListener;
import org.bidib.wizard.mvc.main.view.component.DefaultTabSelectionPanel;
import org.bidib.wizard.mvc.main.view.cvdef.CvNode;
import org.bidib.wizard.mvc.main.view.cvdef.LongCvNode;
import org.bidib.wizard.mvc.main.view.menu.BasicPopupMenu;
import org.bidib.wizard.mvc.main.view.panel.listener.TabSelectionListener;
import org.bidib.wizard.mvc.main.view.panel.listener.TabStatusListener;
import org.bidib.wizard.mvc.main.view.statusbar.StatusBar;
import org.bidib.wizard.mvc.main.view.table.PortHighlighter;
import org.bidib.wizard.mvc.stepcontrol.model.StepControlAspect;
import org.bidib.wizard.mvc.stepcontrol.model.StepControlAspect.Polarity;
import org.bidib.wizard.mvc.stepcontrol.model.StepControlModel;
import org.bidib.wizard.mvc.stepcontrol.model.StepControlModel.TurnTableType;
import org.bidib.wizard.utils.ImageUtils;
import org.bidib.wizard.utils.IntegerEditor;
import org.jdesktop.swingx.JXTable;
import org.jdesktop.swingx.decorator.Highlighter;
import org.jdesktop.swingx.decorator.HighlighterFactory;
import org.jdesktop.swingx.sort.TableSortController;
import org.jdesktop.swingx.treetable.TreeTableNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jgoodies.binding.PresentationModel;
import com.jgoodies.binding.adapter.BasicComponentFactory;
import com.jgoodies.binding.adapter.SingleListSelectionAdapter;
import com.jgoodies.binding.list.SelectionInList;
import com.jgoodies.binding.value.ConverterValueModel;
import com.jgoodies.binding.value.ValueHolder;
import com.jgoodies.binding.value.ValueModel;
import com.jgoodies.forms.builder.ButtonBarBuilder;
import com.jgoodies.forms.builder.DefaultFormBuilder;
import com.jgoodies.forms.factories.Borders;
import com.jgoodies.forms.layout.FormLayout;
import com.vlsolutions.swing.toolbars.ToolBarConstraints;
import com.vlsolutions.swing.toolbars.ToolBarPanel;
import com.vlsolutions.swing.toolbars.VLToolBar;

import eu.hansolo.steelseries.extras.Led;

public class StepControlPanel implements TabSelectionListener, CvDefintionPanelProvider {
    private static final Logger LOGGER = LoggerFactory.getLogger(StepControlPanel.class);

    private static final String READ = "read";

    private static final String WRITE = "write";

    private static final String KEYWORD_PATTERN_POLARITY = "target_polarity_%d";

    private static final String KEYWORD_PATTERN_POSITION = "target_position_%d";

    private static final String KEYWORD_STEP_POSITION = "step_position";

    private static final String KEYWORD_CONFIGURED_ASPECTS = "configured_aspects";

    private static final String KEYWORD_TABLETYPE = "tabletype";

    private static final int EXTRA_VERTICAL_SPACE = 4;

    private static final int MAX_CONFIGURED_ASPECTS = 48;

    private static final long INACTIVE_POSITION_VALUE = 0xFFFFFFFFL;

    private final StepControlModel stepControlModel;

    private final TabStatusListener tabStatusListener;

    private JXTable table;

    private JPanel component;

    private Node selectedNode;

    private SelectionInList aspectSelection;

    private PresentationModel stepControlPresentationModel;

    private ImageIcon accessoryErrorIcon;

    private ImageIcon accessorySuccessfulIcon;

    private ImageIcon accessoryWaitIcon;

    private ImageIcon accessoryUnknownIcon;

    private JLabel executionStateIconLabel = new JLabel();

    private AccessoryExecutionState accessoryExecutionState;

    private List cvDefinitionRequestListeners =
        new LinkedList();

    private Map mapKeywordToNode;

    private List requiredConfigVariables = new LinkedList<>();

    private final MainModel mainModel;

    private boolean initialized;

    private StatusBar statusBar;

    private List fieldsToUpdate = new LinkedList<>();

    private List occupancyLeds = new LinkedList<>();

    private JButton readButton;

    private JButton writeButton;

    private VLToolBar toolbarCvDefinition;

    public StepControlPanel(final MainModel mainModel, final TabStatusListener tabStatusListener) {
        this.tabStatusListener = tabStatusListener;
        this.mainModel = mainModel;

        stepControlModel = new StepControlModel();

        statusBar = DefaultApplicationContext.getInstance().get("statusBar", StatusBar.class);
    }

    public void createComponent() {

        DefaultTabSelectionPanel container = new DefaultTabSelectionPanel() {
            private static final long serialVersionUID = 1L;

            @Override
            public void tabSelected(boolean selected) {
                LOGGER.debug("Select tab, current component: is StepControlPanel.");

                StepControlPanel.this.tabSelected(selected);
            }
        };

        DefaultFormBuilder defaultFormBuilder = new DefaultFormBuilder(new FormLayout("p, 3dlu, p:g"), container);
        defaultFormBuilder.border(Borders.TABBED_DIALOG);

        initializeAccessoryStateIcons();

        ImageIcon iconReadAndCreate =
            ImageUtils.createImageIcon(StepControlPanel.class, "/icons/stepcontrol/SetCurrentPosition.png");
        ImageIcon iconReadPosition =
            ImageUtils.createImageIcon(StepControlPanel.class, "/icons/stepcontrol/ReadPosition.png");

        aspectSelection =
            new SelectionInList<>((ListModel) stepControlModel.getStepControlAspectsListModel());

        stepControlPresentationModel = new PresentationModel<>(new ValueHolder(stepControlModel, true));

        executionStateIconLabel = new JLabel();
        defaultFormBuilder.append("Execution state: ", executionStateIconLabel);

        defaultFormBuilder.appendRow("5dlu");

        table =
            new JXTable(
                new AspectTableAdapter(
                    aspectSelection, new String[] { Resources.getString(StepControlPanel.class, "position"),
                        Resources.getString(StepControlPanel.class, "polarity"),
                        Resources.getString(StepControlPanel.class, "action") }) {

                    private static final long serialVersionUID = 1L;

                    @Override
                    public void fireTableDataChanged() {
                        // preserve selection calling fireTableDataChanged()

                        int[] sel = null;
                        int[] index = null;
                        if (table != null) {
                            sel = table.getSelectedRows();
                            index = new int[1];
                            index[0] = table.getRowSorter().convertRowIndexToModel(sel[0]);
                            LOGGER.info("tableDataChanged, sel: {}, index: {}", sel, index);
                        }

                        super.fireTableDataChanged();

                        if (index != null) {
                            setSelectedIndex(index[0]);
                        }
                    }
                }) {
                private static final long serialVersionUID = 1L;

                @Override
                public boolean editCellAt(final int row, int column, EventObject e) {
                    boolean isEditing = super.editCellAt(row, column, e);
                    LOGGER.info("isEditing: {}", isEditing);

                    // this is necessary for the polarity toggle button to work correct
                    if (column == AspectTableAdapter.COLUMN_POLARITY) {
                        SwingUtilities.invokeLater(new Runnable() {
                            public void run() {
                                if (getCellEditor() != null) {
                                    getCellEditor().stopCellEditing();
                                    // keep selection
                                    getSelectionModel().setSelectionInterval(row, row);
                                }
                            }
                        });
                    }
                    return isEditing;
                }

                protected RowSorter createDefaultRowSorter() {
                    return new TableSortController(
                        getModel()) {
                        @Override
                        public void toggleSortOrder(int column) {
                            LOGGER.debug("toggleSortOrder on column: {}", column);
                            // do nothing to prevent toggle sort order
                        }
                    };
                }
            };
        table.setSelectionModel(new SingleListSelectionAdapter(aspectSelection.getSelectionIndexHolder()));
        table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);

        table.setRowHeight(table.getRowHeight() + EXTRA_VERTICAL_SPACE);
        table.getColumn(AspectTableAdapter.COLUMN_POSITION).setPreferredWidth(100);
        table.getColumn(AspectTableAdapter.COLUMN_POLARITY).setPreferredWidth(70);

        // add highlighter that evaluate if the port is enabled
        PortHighlighter portHighlighter = new PortHighlighter();
        Highlighter simpleStriping = HighlighterFactory.createSimpleStriping();
        table.setHighlighters(simpleStriping, portHighlighter);

        JScrollPane scrollPane = new JScrollPane(table);
        scrollPane.setPreferredSize(table.getPreferredSize());

        LineNumberTableRowHeader tableLineNumber = new LineNumberTableRowHeader(scrollPane, table);
        tableLineNumber.setBackground(Color.LIGHT_GRAY);
        tableLineNumber.setVerticalOffset(EXTRA_VERTICAL_SPACE);
        scrollPane.setRowHeaderView(tableLineNumber);

        table.getModel().addTableModelListener(new TableModelListener() {

            @Override
            public void tableChanged(TableModelEvent e) {
                LOGGER.info("The table has changed, e: {}", e);

                tabStatusListener.updatePendingChanges(component, true);
            }
        });

        defaultFormBuilder.appendRow("fill:200dlu:g");
        defaultFormBuilder.nextLine(2);
        defaultFormBuilder.append(scrollPane);

        // create the 'detail panel'
        DefaultFormBuilder detailFormBuilder = new DefaultFormBuilder(new FormLayout("p, 3dlu, p, 3dlu, p, 3dlu, p:g"));

        // use converter for turntable model
        final ValueModel turnTableTypeConverterModel =
            new ConverterValueModel(
                stepControlPresentationModel.getBufferedModel(StepControlModel.PROPERTYNAME_TURNTABLE_TYPE),
                new TurnTableTypeConverter());
        JTextField turnTableType = BasicComponentFactory.createTextField(turnTableTypeConverterModel);
        turnTableType.setEditable(false);
        detailFormBuilder.append(Resources.getString(StepControlPanel.class, "turntableType"), turnTableType, 5);
        detailFormBuilder.nextLine();

        final ValueModel currentPositionConverterModel =
            new ConverterValueModel(
                stepControlPresentationModel.getBufferedModel(StepControlModel.PROPERTYNAME_CURRENT_POSITION),
                new StringToUnsignedLongConverter());
        JTextField currentPosition = BasicComponentFactory.createTextField(currentPositionConverterModel);
        detailFormBuilder.append(Resources.getString(StepControlPanel.class, "position"), currentPosition, 5);
        detailFormBuilder.nextLine();

        // add the occupancy leds
        Led occupancy1 = new Led();
        occupancy1.setPreferredSize(new Dimension(32, 32));
        Led occupancy2 = new Led();
        occupancy2.setPreferredSize(new Dimension(32, 32));
        Led occupancy3 = new Led();
        occupancy3.setPreferredSize(new Dimension(32, 32));
        Led occupancy4 = new Led();
        occupancy4.setPreferredSize(new Dimension(32, 32));

        occupancyLeds.add(occupancy1);
        occupancyLeds.add(occupancy2);
        occupancyLeds.add(occupancy3);
        occupancyLeds.add(occupancy4);

        detailFormBuilder.append(Resources.getString(StepControlPanel.class, "occupancy"),
            buildLeftAlignedButtonBar(occupancy1, occupancy2, occupancy3, occupancy4), 5);
        detailFormBuilder.nextLine();

        // lower part of the detail form

        JButton readCurrentPosAndCreateAspect = new JButton(iconReadAndCreate);
        readCurrentPosAndCreateAspect.setMargin(new Insets(0, 0, 0, 0));
        readCurrentPosAndCreateAspect.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                LOGGER.info("Create new aspect from data in form.");

                fireCreateNewAspect();
            }
        });
        JButton readCurrentPos = new JButton(iconReadPosition);
        readCurrentPos.setMargin(new Insets(0, 0, 0, 0));
        readCurrentPos.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                LOGGER.info("Read current position.");
                fireReadPos();
            }
        });

        detailFormBuilder.append(buildLeftAlignedButtonBar(readCurrentPosAndCreateAspect, readCurrentPos), 7);

        defaultFormBuilder.append(detailFormBuilder.build());

        component = defaultFormBuilder.build();

        // the name is displayed on the tab
        component.setName(getName());

        final AspectTablePopupMenu menu = new AspectTablePopupMenu();

        table.addMouseListener(new MouseAdapter() {
            public void mousePressed(MouseEvent e) {
                handleMouseEvent(e, menu);
            }

            public void mouseReleased(MouseEvent e) {
                handleMouseEvent(e, menu);
            }
        });
        // do not allow drag columns to other position
        table.getTableHeader().setReorderingAllowed(false);

        table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);

        table.getColumn(AspectTableAdapter.COLUMN_POLARITY).setCellEditor(
            new CustomBooleanCellEditor("/icons/stepcontrol/arrow_right.png", "/icons/stepcontrol/arrow_left.png"));
        table.getColumn(AspectTableAdapter.COLUMN_POLARITY).setCellRenderer(
            new CustomBooleanCellRenderer("/icons/stepcontrol/arrow_right.png", "/icons/stepcontrol/arrow_left.png"));

        table.getColumn(AspectTableAdapter.COLUMN_POSITION).setCellRenderer(new LongRenderer());
        table.getColumn(AspectTableAdapter.COLUMN_POSITION).setCellEditor(new IntegerEditor(0, Integer.MAX_VALUE));

        table.getColumnExt(AspectTableAdapter.COLUMN_POLARITY).setSortable(false);
        table.getColumnExt(AspectTableAdapter.COLUMN_PERFORM_ASPECT).setSortable(false);
        table.setSortOrder(AspectTableAdapter.COLUMN_POSITION, SortOrder.ASCENDING);

        Action performAspect = new AbstractAction() {
            private static final long serialVersionUID = 1L;

            public void actionPerformed(ActionEvent e) {
                int modelRow = Integer.valueOf(e.getActionCommand());
                firePerformAspect(modelRow);
            }
        };

        // add the button renderer to the column
        new ButtonColumn(table, performAspect, AspectTableAdapter.COLUMN_PERFORM_ASPECT);

        // add the pending changes listener to the stepControlModel
        stepControlModel.addPropertyChangeListener(new PropertyChangeListener() {

            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                LOGGER.info("Property has changed, evt: {}", evt);
                tabStatusListener.updatePendingChanges(component, true);
            }
        });

        toolbarCvDefinition = new VLToolBar("stepControlCvDefinition");
        addButtons(toolbarCvDefinition);
        addToolBar(toolbarCvDefinition, new ToolBarConstraints(0, 2));
        // initially invisible
        toolbarCvDefinition.setVisible(false);

        mainModel.addFeedbackPortListener(new DefaultFeedbackPortListener() {

            @Override
            public void statusChanged(Port port, FeedbackPortStatus status) {

                if (port.getId() < occupancyLeds.size()) {
                    LOGGER.info("The status of the feedback port has changed: {}, status: {}", port, status);
                    Led occupancyLed = occupancyLeds.get(port.getId());
                    if (occupancyLed != null) {

                        occupancyLed.setLedOn(status == FeedbackPortStatus.OCCUPIED ? true : false);
                    }
                }
                else {
                    LOGGER.debug("Port number of feedback port is out of range: {}", port.getId());
                }
            }
        });
    }

    /**
     * Initialize the accessory state icons
     */
    private void initializeAccessoryStateIcons() {
        // Set the icon for leaf nodes.
        accessoryErrorIcon = ImageUtils.createImageIcon(StepControlPanel.class, "/icons/accessory-error.png");
        accessorySuccessfulIcon = ImageUtils.createImageIcon(StepControlPanel.class, "/icons/accessory-successful.png");
        accessoryWaitIcon = ImageUtils.createImageIcon(StepControlPanel.class, "/icons/accessory-wait.png");
        accessoryUnknownIcon = ImageUtils.createImageIcon(StepControlPanel.class, "/icons/accessory-unknown.png");
    }

    private void firePerformAspect(int row) {
        LOGGER.info("Aspect is performed on row: {}", row);

        for (CvDefinitionRequestListener l : cvDefinitionRequestListeners) {
            l.activateAspect(row);
        }
    }

    private void readCurrentValuesFromCV(final Node node) {
        LOGGER.info("Read the values from CV for node: {}", node);

        if (node == null || !ProductUtils.isStepControl(node.getUniqueId())) {
            LOGGER.warn("No node available or not a StepControl: {}", node);

            // TODO clear model
            if (mapKeywordToNode != null) {
                mapKeywordToNode.clear();
            }

            return;
        }
        LOGGER.info("Get the cvDefinitionTreeTableModel from the node: {}", node);
        CvDefinitionTreeTableModel cvDefinitionTreeTableModel = node.getCvDefinitionTreeTableModel();
        if (cvDefinitionTreeTableModel != null) {
            // search the keywords
            mapKeywordToNode = new LinkedHashMap<>();

            TreeTableNode rootNode = cvDefinitionTreeTableModel.getRoot();
            if (rootNode != null) {
                harvestKeywordNodes(rootNode, mapKeywordToNode);
            }

            LOGGER.info("Found keywords in nodes: {}", mapKeywordToNode.keySet());
        }
        else {
            LOGGER.warn("No cvDefinitionTreeTableModel available for node: {}", node);
        }

        // TODO make this more clever? only load values that are not loaded already? is this clever? how about changed
        // values?

        // read the values from the node
        if (MapUtils.isNotEmpty(mapKeywordToNode)) {
            List configVariables = new LinkedList<>();
            for (CvNode cvNode : mapKeywordToNode.values()) {
                // LOGGER.info("Process cvNode: {}", cvNode);

                prepareConfigVariables(cvNode, configVariables, node.getCvNumberToNodeMap());
            }

            // keep the list of config variables
            this.requiredConfigVariables.clear();
            if (CollectionUtils.isNotEmpty(configVariables)) {
                this.requiredConfigVariables.addAll(configVariables);
            }

            fireLoadConfigVariables(configVariables);
        }
        else {
            LOGGER.warn("No values available in mapKeywordToNode!");
        }
    }

    private void prepareConfigVariables(
        final CvNode cvNode, final List configVariables,
        final Map cvNumberToNodeMap) {

        try {
            switch (cvNode.getCV().getType()) {
                case LONG:
                    // LONG nodes are processed
                    LongCvNode masterNode = ((LongCvNode) cvNode).getMasterNode();
                    configVariables.add(masterNode.getConfigVar());
                    for (CvNode slaveNode : masterNode.getSlaveNodes()) {
                        configVariables.add(slaveNode.getConfigVar());
                    }
                    break;
                case INT:
                    // INT nodes are processed
                    configVariables.add(cvNode.getConfigVar());
                    int highCvNum = Integer.parseInt(cvNode.getCV().getHigh());
                    if (highCvNum == cvNode.getCV().getNumber()) {
                        // search the low CV
                        CvNode lowCvNode = cvNumberToNodeMap.get(cvNode.getCV().getLow());
                        configVariables.add(lowCvNode.getConfigVar());
                    }
                    else {
                        // search the high CV
                        CvNode highCvNode = cvNumberToNodeMap.get(cvNode.getCV().getHigh());
                        configVariables.add(highCvNode.getConfigVar());
                    }
                    break;
                default:
                    configVariables.add(cvNode.getConfigVar());
                    break;
            }
        }
        catch (Exception ex) {
            LOGGER.warn("Prepare config variables to read from node failed.", ex);
        }

    }

    private void harvestKeywordNodes(TreeTableNode parentNode, Map mapKeywordToNode) {

        if (parentNode != null) {
            for (int index = 0; index < parentNode.getChildCount(); index++) {
                TreeTableNode childNode = parentNode.getChildAt(index);
                if (childNode instanceof CvNode) {
                    CvNode cvNode = (CvNode) childNode;
                    if (StringUtils.isNotBlank(cvNode.getCV().getKeyword())) {
                        LOGGER.info("The current node has a keyword: {}", cvNode.getCV().getKeyword());

                        mapKeywordToNode.put(cvNode.getCV().getKeyword(), cvNode);
                    }

                }
                harvestKeywordNodes(childNode, mapKeywordToNode);
            }
        }
    }

    public void addCvDefinitionRequestListener(CvDefinitionRequestListener l) {
        cvDefinitionRequestListeners.add(l);
    }

    private void fireLoadConfigVariables(List configVariables) {
        // TODO decouple the AWT-thread from this work?
        LOGGER.info("Load the config variables.");
        for (CvDefinitionRequestListener l : cvDefinitionRequestListeners) {
            l.loadCvValues(configVariables);
        }
    }

    private JComponent buildButtonBar(JButton... button) {
        return new ButtonBarBuilder().addGlue().addButton(button).build();
    }

    private JComponent buildLeftAlignedButtonBar(JComponent... button) {
        return new ButtonBarBuilder().addButton(button).addGlue().build();
    }

    public JPanel getComponent() {
        return component;
    }

    public String getName() {
        return Resources.getString(getClass(), "name");
    }

    @Override
    public void tabSelected(boolean selected) {
        LOGGER.info("Tab is selected: {}, initialized: {}", selected, initialized);
        // show the toolbar
        toolbarCvDefinition.setVisible(selected);

        if (selected && !initialized) {
            statusBar.setStatus(Resources.getString(StepControlPanel.class, "loading_cvvalues"));

            // call this method after node was selected
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    readCurrentValuesFromCV(selectedNode);
                }
            });
        }

        if (!selected && mainModel.getSelectedNode() == null) {
            LOGGER.info("The tab is no longer selected and the selected node is null, reset the selected node!");

            selectedNode = null;
        }
    }

    private void handleMouseEvent(MouseEvent e, AspectTablePopupMenu aspectTableMenu) {
        if (e.isPopupTrigger()) {
            int row = table.rowAtPoint(e.getPoint());
            int column = table.columnAtPoint(e.getPoint());
            showAspectTableMenu(e, aspectTableMenu, row, column);
        }
    }

    private void showAspectTableMenu(MouseEvent e, AspectTablePopupMenu aspectTableMenu, int row, int column) {

        Object value = null;
        if (row > -1) {
            table.setRowSelectionInterval(row, row);
            value = table.getValueAt(row, -1);
        }
        aspectTableMenu.setDeleteEnabled(value instanceof StepControlAspect);

        aspectTableMenu.show(e.getComponent(), e.getX(), e.getY());
    }

    private final class AspectTablePopupMenu extends BasicPopupMenu {
        private static final long serialVersionUID = 1L;

        private JMenuItem newLabel;

        private JMenuItem deleteLabel;

        public AspectTablePopupMenu() {
            newLabel = new JMenuItem(Resources.getString(StepControlPanel.class, "newAspect") + " ...");
            newLabel.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {

                    if (stepControlModel.getStepControlAspects().size() < MAX_CONFIGURED_ASPECTS) {
                        int stepNumber = table.getRowCount() + 1;
                        StepControlAspect aspect = new StepControlAspect(stepNumber, 0, Polarity.normal);

                        stepControlModel.addStepControlAspect(aspect);
                    }
                    else {
                        LOGGER.warn("Maximum number of aspects reached.");
                        JOptionPane.showMessageDialog(component,
                            Resources.getString(StepControlPanel.class, "max_aspect_message"),
                            Resources.getString(StepControlPanel.class, "max_aspect_title"), JOptionPane.ERROR_MESSAGE);
                    }

                }
            });
            add(newLabel);

            deleteLabel = new JMenuItem(Resources.getString(StepControlPanel.class, "deleteAspect") + " ...");
            deleteLabel.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    int selectedRow = table.getSelectedRow();
                    if (selectedRow > -1) {

                        Object aspect = table.getValueAt(selectedRow, -1);
                        if (aspect instanceof StepControlAspect) {
                            stepControlModel.removeStepControlAspect((StepControlAspect) aspect);
                        }
                    }
                }
            });
            add(deleteLabel);
        }

        public void setDeleteEnabled(boolean enabled) {
            deleteLabel.setEnabled(enabled);
        }
    }

    private void fireCreateNewAspect() {

        if (stepControlModel.getStepControlAspects().size() < MAX_CONFIGURED_ASPECTS) {

            // create new aspect using the 'actual position'
            Long position =
                (Long) stepControlPresentationModel.getBufferedValue(StepControlModel.PROPERTYNAME_CURRENT_POSITION);
            if (position == null) {
                position = 0L;
            }
            StepControlAspect stepControlAspect = new StepControlAspect(null, position, Polarity.normal);

            stepControlModel.addStepControlAspect(stepControlAspect);
            aspectSelection.setSelection(stepControlAspect);

            int index = aspectSelection.getSelectionIndex();
            setSelectedIndex(index);
        }
        else {
            LOGGER.warn("Maximum number of aspects reached.");
            JOptionPane.showMessageDialog(component, Resources.getString(StepControlPanel.class, "max_aspect_message"),
                Resources.getString(StepControlPanel.class, "max_aspect_title"), JOptionPane.ERROR_MESSAGE);
        }
    }

    private void setSelectedIndex(int index) {
        index = table.getRowSorter().convertRowIndexToView(index);
        table.getSelectionModel().setSelectionInterval(index, index);

        if (true) {
            Rectangle cellRect = table.getCellRect(index, 0, false);
            if (cellRect != null) {
                table.scrollRectToVisible(cellRect);
            }
        }
    }

    private void fireReadPos() {
        LOGGER.info("Read the current position.");

        CvNode cvNode = mapKeywordToNode.get(KEYWORD_STEP_POSITION);
        if (cvNode != null) {
            List configVariables = new LinkedList<>();

            prepareConfigVariables(cvNode, configVariables, selectedNode.getCvNumberToNodeMap());

            fireLoadConfigVariables(configVariables);
        }
        else {
            LOGGER.warn("The step_position CV is not available.");
        }
    }

    /**
     * The CV definition of the node has changed.
     */
    public void cvDefinitionChanged() {

        LOGGER.info("The cv definition has changed, selected node: {}", selectedNode);

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

        if (readButton != null) {
            readButton.setEnabled(false);
        }
        if (writeButton != null) {
            writeButton.setEnabled(false);
        }

        selectedNode = mainModel.getSelectedNode();

        // clear the aspects
        stepControlModel.clearModel();
        requiredConfigVariables.clear();
        if (mapKeywordToNode != null) {
            LOGGER.info("Clear the mapKeywordToNode.");
            mapKeywordToNode.clear();
        }

        // force reload of CV values
        initialized = false;

        if (selectedNode != null) {
            if (readButton != null) {
                // enable the read button to get the values of the configuration variables for this node on
                // request
                readButton.setEnabled(CollectionUtils.isNotEmpty(selectedNode.getConfigVariables()));
            }
            if (writeButton != null) {
                // enable the write button to write the values of the configuration variables for this node on
                // request
                writeButton.setEnabled(CollectionUtils.isNotEmpty(selectedNode.getConfigVariables()));
            }
        }

        // load the aspects initially
        if (selectedNode != null && ProductUtils.isStepControl(selectedNode.getUniqueId())) {
            LOGGER.info("The currently selected node is a StepControl.");
            fieldsToUpdate.add(KEYWORD_CONFIGURED_ASPECTS);
        }
    }

    /**
     * The values of the current CV definition have changed.
     */
    public void cvDefinitionValuesChanged() {
        LOGGER.info("The cv defintion values have changed.");

        if (selectedNode == null || !ProductUtils.isStepControl(selectedNode.getUniqueId())) {
            LOGGER.info("The currently selected node is not a StepControl.");
            return;
        }

        if (MapUtils.isEmpty(mapKeywordToNode)) {
            LOGGER.info("No mapKeywordToNode value available.");
            return;
        }

        try {
            Integer value = getConfigVarIntValue(KEYWORD_TABLETYPE);
            TurnTableType turnTableType = TurnTableType.fromValue(ByteUtils.getLowByte(value));
            stepControlModel.setTurnTableType(turnTableType);
        }
        catch (Exception ex) {
            LOGGER.warn("Set the turnTableType failed.", ex);
        }

        try {
            Long stepPosition1 = getConfigVarLongValue(KEYWORD_STEP_POSITION);
            stepControlModel.setCurrentPosition(stepPosition1);
        }
        catch (Exception ex) {
            LOGGER.warn("Set the current position failed.", ex);
        }

        if (fieldsToUpdate.contains(KEYWORD_CONFIGURED_ASPECTS)) {
            LOGGER.info("Prepare the configured aspects.");

            // create the list of aspects with the values from the config variables
            List configuredAspects = new LinkedList<>();
            for (int index = 0; index < MAX_CONFIGURED_ASPECTS; index++) {
                try {
                    Integer targetPolarity = getConfigVarIntValue(String.format(KEYWORD_PATTERN_POLARITY, index));
                    Long targetPosition = getConfigVarLongValue(String.format(KEYWORD_PATTERN_POSITION, index));

                    if (targetPosition < INACTIVE_POSITION_VALUE) {

                        StepControlAspect stepControlAspect =
                            new StepControlAspect(null, targetPosition.longValue(), Polarity.valueOf(targetPolarity
                                .intValue()));
                        LOGGER.info("Adding new stepControlAspect: {}", stepControlAspect);
                        configuredAspects.add(stepControlAspect);
                    }
                    else {
                        LOGGER.info("No active position at index {}, skip further creation of aspects, position: {}",
                            index, targetPosition);
                        break;
                    }
                }
                catch (IllegalArgumentException ex) {
                    LOGGER.warn("Prepare configured step control aspect failed: {}", ex.getMessage());
                }
                catch (Exception ex) {
                    LOGGER.warn("Prepare configured step control aspect failed.", ex);
                }
            }
            stepControlModel.setStepControlAspects(configuredAspects);
            fieldsToUpdate.remove(KEYWORD_CONFIGURED_ASPECTS);
        }

        if (!initialized) {

            initialized = true;

            LOGGER.info("Reset the pending changes flag on the tab.");
            tabStatusListener.updatePendingChanges(component, false);
        }

    }

    private CvNode getNode(String keyword) {
        CvNode cvNode = mapKeywordToNode.get(keyword);
        return cvNode;
    }

    private Integer getConfigVarIntValue(String keyword) {
        CvNode cvNode = getNode(keyword);
        Integer value = null;
        if (cvNode != null) {
            switch (cvNode.getCV().getType()) {
                case INT:
                    // process integer values
                    value = Integer.parseInt(cvNode.getConfigVar().getValue());
                    int highCvNum = Integer.parseInt(cvNode.getCV().getHigh());
                    if (highCvNum == cvNode.getCV().getNumber()) {
                        // search the low CV
                        CvNode lowCvNode = selectedNode.getCvNumberToNodeMap().get(cvNode.getCV().getLow());
                        value = (value << 8) | Integer.parseInt(lowCvNode.getConfigVar().getValue());
                    }
                    else {
                        // search the high CV
                        CvNode highCvNode = selectedNode.getCvNumberToNodeMap().get(cvNode.getCV().getHigh());
                        value = (value << 8) | Integer.parseInt(highCvNode.getConfigVar().getValue());
                    }
                    break;
                default:
                    value = Integer.parseInt(cvNode.getConfigVar().getValue());
                    break;
            }
        }
        return value;
    }

    private Long getConfigVarLongValue(String keyword) {
        CvNode cvNode = getNode(keyword);
        Long value = null;
        if (cvNode != null) {
            switch (cvNode.getCV().getType()) {
                case LONG:
                    // process long values
                    LongCvNode masterNode = ((LongCvNode) cvNode).getMasterNode();
                    long longValue = 0;
                    CvNode[] slaveNodes = masterNode.getSlaveNodes().toArray(new CvNode[0]);
                    for (int index = 2; index > -1; index--) {
                        CvNode slaveNode = slaveNodes[index];
                        longValue = (longValue << 8) | Integer.parseInt(slaveNode.getConfigVar().getValue());
                    }
                    longValue = (longValue << 8) | Integer.parseInt(masterNode.getConfigVar().getValue());
                    value = ((long) longValue) & 0xffffffffL;
                    break;
                default:
                    break;
            }
        }
        return value;
    }

    public void executionStateChanged(
        AccessoryExecutionState executionState, int accessoryId, int aspect, AccessoryState accessoryState) {
        LOGGER.debug("The execution state has changed: {}, accessoryId: {}, aspect: {}", executionState, accessoryId,
            aspect);
        if (aspect == -1 || executionState == null) {
            executionState = AccessoryExecutionState.IDLE;
        }

        executionStateIconLabel.setToolTipText(null);
        switch (executionState) {
            case ERROR:
                executionStateIconLabel.setIcon(accessoryErrorIcon);
                if (accessoryState != null) {
                    executionStateIconLabel.setToolTipText(accessoryState.getErrorInformation());
                }
                break;
            case RUNNING:
                executionStateIconLabel.setIcon(accessoryWaitIcon);
                break;
            case SUCCESSFUL:
                executionStateIconLabel.setIcon(accessorySuccessfulIcon);
                break;
            case UNKNOWN:
                executionStateIconLabel.setIcon(accessoryUnknownIcon);
                break;
            default:
                executionStateIconLabel.setIcon(null);
                break;
        }

    }

    static class LongRenderer extends DefaultTableCellRenderer.UIResource {
        private static final long serialVersionUID = 1L;

        private NumberFormat formatter;

        public LongRenderer() {
            super();
            setHorizontalAlignment(JLabel.RIGHT);
        }

        public void setValue(Object value) {
            if (formatter == null) {
                formatter = new DecimalFormat("#");
            }

            if (value instanceof Number) {
                long sourceValue = ((Number) value).longValue();
                long lValue = ((long) sourceValue) & 0xffffffffL;

                setText(formatter.format(lValue));
            }
            else {
                setText(null);
            }
        }
    }

    private void addButtons(VLToolBar toolBar) {

        // read button
        readButton =
            makeNavigationButton("load-from-node", "/32x32", READ,
                Resources.getString(getClass(), "toolbar.readallcv"),
                Resources.getString(getClass(), "toolbar.readallcv.alttext"));
        readButton.setEnabled(false);
        toolBar.add(readButton);

        readButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                readCurrentValuesFromCV(selectedNode);
            }
        });

        // write button
        writeButton =
            makeNavigationButton("save-to-node", "/32x32", WRITE,
                Resources.getString(getClass(), "toolbar.writeallcv"),
                Resources.getString(getClass(), "toolbar.writeallcv.alttext"));
        toolBar.add(writeButton);
        writeButton.setEnabled(false);

        writeButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                LOGGER.info("Write the CV values.");

                // prepare the cv list to write
                final List cvList = new LinkedList<>();

                // collect the new values
                int aspectIndex = 0;

                List aspects = new LinkedList<>();
                // get the aspects from the model
                aspects.addAll(stepControlModel.getStepControlAspects());

                Collections.sort(aspects, new Comparator() {

                    @Override
                    public int compare(StepControlAspect o1, StepControlAspect o2) {
                        return Long.compare(o1.getPosition(), o2.getPosition());
                    }
                });

                // check the aspects
                for (StepControlAspect currentAspect : aspects) {
                    LOGGER.info("Prepare aspect to save: {}", currentAspect);

                    prepareCvValues(aspectIndex, currentAspect, cvList);

                    aspectIndex++;
                }

                // add the terminating aspects by setting 0xFFFF as position
                if (aspectIndex < MAX_CONFIGURED_ASPECTS) {
                    StepControlAspect currentAspect =
                        new StepControlAspect(null, INACTIVE_POSITION_VALUE, Polarity.normal);
                    prepareCvValues(aspectIndex, currentAspect, cvList);

                    aspectIndex++;
                }

                // check the turntable type
                CvNode tableTypeNode = getNode(KEYWORD_TABLETYPE);
                TurnTableType turnTableType = stepControlModel.getTurnTableType();
                CvValueUtils.compareAndAddNewValue(tableTypeNode, Integer.toString(turnTableType.getCvValue()), cvList);

                // write the values on the node
                CvValueUtils.writeCvValues(cvList, selectedNode.getCvNumberToNodeMap(), StepControlPanel.this);
            }
        });
    }

    private void prepareCvValues(
        int aspectIndex, StepControlAspect currentAspect, final List cvList) {

        // prepare the keyword to search
        String keywordPolarity = String.format(KEYWORD_PATTERN_POLARITY, aspectIndex);
        CvNode polarityNode = getNode(keywordPolarity);

        Polarity polarity = currentAspect.getPolarity();
        CvValueUtils.compareAndAddNewValue(polarityNode, Integer.toString(polarity.getCvValue()), cvList);

        // the target position is a long type
        String keywordPosition = String.format(KEYWORD_PATTERN_POSITION, aspectIndex);
        // get the master
        LongCvNode positionNode = (LongCvNode) getNode(keywordPosition);

        int position = (int) currentAspect.getPosition();
        // prepare the master value
        LOGGER.info("The new position is: {}", position);
        byte[] bytes = new byte[4];
        bytes[0] = ByteUtils.getLowByte(position);
        bytes[1] = ByteUtils.getHighByte(position);

        bytes[2] = ByteUtils.getHighWordLowByte(position);
        bytes[3] = ByteUtils.getHighWordHighByte(position);

        LOGGER.debug("Current slave CV#: {}, value: {}", positionNode.getCV().getNumber(), ByteUtils.getInt(bytes[0]));
        CvValueUtils.compareAndAddNewValue(positionNode, Integer.toString(ByteUtils.getInt(bytes[0])), cvList);
        // add the slaves
        int idx = 1;
        for (CvNode slaveNode : positionNode.getSlaveNodes()) {
            LOGGER.debug("Current slave CV#: {}, value: {}", slaveNode.getCV().getNumber(),
                ByteUtils.getInt(bytes[idx]));
            CvValueUtils.compareAndAddNewValue(slaveNode, Integer.toString(ByteUtils.getInt(bytes[idx])), cvList);

            idx++;
        }
    }

    private JButton makeNavigationButton(
        String imageName, String pathExt, String actionCommand, String toolTipText, String altText) {
        // Look for the image.
        String imgLocation = "/icons/" + imageName + ".png";
        if (pathExt != null) {
            imgLocation = "/icons" + pathExt + "/" + imageName + ".png";
        }
        URL imageURL = StepControlPanel.class.getResource(imgLocation);

        // Create and initialize the button.
        JButton button = new JButton();
        button.setActionCommand(actionCommand);
        button.setToolTipText(toolTipText);

        if (imageURL != null) { // image found
            button.setIcon(new ImageIcon(imageURL, altText));
        }
        else { // no image found
            button.setText(altText);
            LOGGER.warn("Resource not found: {}", imgLocation);
        }

        return button;
    }

    private void addToolBar(final VLToolBar toolBar, ToolBarConstraints constraints) {
        ToolBarPanel topToolBarPanel = (ToolBarPanel) DefaultApplicationContext.getInstance().get("topToolBarPanel");
        topToolBarPanel.add(toolBar, constraints);
    }

    private void removeToolBar(final VLToolBar toolBar) {
        ToolBarPanel topToolBarPanel = (ToolBarPanel) DefaultApplicationContext.getInstance().get("topToolBarPanel");
        topToolBarPanel.remove(toolBar);
    }

    @Override
    public void checkPendingChanges() {
        boolean hasPendingChanges = hasPendingChanges();
        if (!hasPendingChanges) {
            tabStatusListener.updatePendingChanges(component, false);
        }
    }

    private boolean hasPendingChanges() {
        // TODO implement hasPendingChanges()
        return false;
    }

    @Override
    public void writeConfigVariables(List cvList) {
        fireWriteConfigVariables(cvList);
    }

    private void fireWriteConfigVariables(List cvList) {
        // TODO decouple the AWT-thread from this work?

        for (CvDefinitionRequestListener l : cvDefinitionRequestListeners) {
            l.writeCvValues(cvList);
        }
    }

    @Override
    public void refreshDisplayedValues() {

    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy