org.bidib.wizard.mvc.stepcontrol.view.StepControlPanel Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bidibwizard-client Show documentation
Show all versions of bidibwizard-client Show documentation
jBiDiB BiDiB Wizard Client Application POM
package org.bidib.wizard.mvc.stepcontrol.view;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import javax.swing.BorderFactory;
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.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.ListModel;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.filechooser.FileNameExtensionFilter;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.bidib.jbidibc.core.node.ConfigurationVariable;
import org.bidib.jbidibc.exchange.vendorcv.CVType;
import org.bidib.jbidibc.exchange.vendorcv.DataType;
import org.bidib.jbidibc.messages.AccessoryState;
import org.bidib.jbidibc.messages.Feature;
import org.bidib.jbidibc.messages.enums.AccessoryExecutionState;
import org.bidib.jbidibc.messages.enums.FeatureEnum;
import org.bidib.jbidibc.messages.utils.ByteUtils;
import org.bidib.jbidibc.messages.utils.ProductUtils;
import org.bidib.wizard.api.locale.Resources;
import org.bidib.wizard.api.model.Accessory;
import org.bidib.wizard.api.model.NodeInterface;
import org.bidib.wizard.api.model.SwitchingNodeInterface;
import org.bidib.wizard.api.model.listener.CvDefinitionRequestListener;
import org.bidib.wizard.api.model.listener.DefaultFeedbackPortListener;
import org.bidib.wizard.api.model.listener.PortValueListener;
import org.bidib.wizard.api.service.console.ConsoleColor;
import org.bidib.wizard.api.utils.NodeUtils;
import org.bidib.wizard.client.common.controller.FeedbackPortStatusChangeProvider;
import org.bidib.wizard.client.common.converter.StringToUnsignedLongConverter;
import org.bidib.wizard.client.common.dialog.EscapeDialog;
import org.bidib.wizard.client.common.text.WizardComponentFactory;
import org.bidib.wizard.client.common.view.BasicPopupMenu;
import org.bidib.wizard.client.common.view.BusyFrame;
import org.bidib.wizard.client.common.view.TabPanelProvider;
import org.bidib.wizard.client.common.view.cvdef.CvDefinitionTreeTableModel;
import org.bidib.wizard.client.common.view.cvdef.CvNode;
import org.bidib.wizard.client.common.view.cvdef.CvNodeUtils;
import org.bidib.wizard.client.common.view.cvdef.LongCvNode;
import org.bidib.wizard.client.common.view.listener.TabStatusListener;
import org.bidib.wizard.client.common.view.statusbar.StatusBar;
import org.bidib.wizard.common.context.DefaultApplicationContext;
import org.bidib.wizard.common.model.settings.ExperimentalSettingsInterface;
import org.bidib.wizard.common.model.settings.WizardSettingsInterface;
import org.bidib.wizard.common.script.switching.AccessoryScripting;
import org.bidib.wizard.common.service.SettingsService;
import org.bidib.wizard.common.utils.ImageUtils;
import org.bidib.wizard.core.dialog.FileDialog;
import org.bidib.wizard.model.ports.FeedbackPort;
import org.bidib.wizard.model.ports.MotorPort;
import org.bidib.wizard.model.ports.SoundPort;
import org.bidib.wizard.model.status.FeedbackPortStatus;
import org.bidib.wizard.model.status.SoundPortStatus;
import org.bidib.wizard.model.status.SpeedSteps;
import org.bidib.wizard.model.stepcontrol.TurnTableType;
import org.bidib.wizard.mvc.common.view.cvdefinition.CvDefinitionPanelProvider;
import org.bidib.wizard.mvc.common.view.cvdefinition.CvValueUtils;
import org.bidib.wizard.mvc.main.controller.CvDefinitionPanelController;
import org.bidib.wizard.mvc.main.model.MainModel;
import org.bidib.wizard.mvc.main.view.component.DefaultTabSelectionPanel;
import org.bidib.wizard.mvc.main.view.panel.listener.TabComponentCreator;
import org.bidib.wizard.mvc.main.view.panel.listener.TabSelectionListener;
import org.bidib.wizard.mvc.stepcontrol.controller.StepControlControllerInterface;
import org.bidib.wizard.mvc.stepcontrol.model.AccelarationScaleEnum;
import org.bidib.wizard.mvc.stepcontrol.model.AspectExecutionModel;
import org.bidib.wizard.mvc.stepcontrol.model.AspectReference;
import org.bidib.wizard.mvc.stepcontrol.model.ConfigurationWizardModel;
import org.bidib.wizard.mvc.stepcontrol.model.ConfigurationWizardModel.WizardStatus;
import org.bidib.wizard.mvc.stepcontrol.model.CvConsoleModel;
import org.bidib.wizard.mvc.stepcontrol.model.Gearing;
import org.bidib.wizard.mvc.stepcontrol.model.MicroStepsEnum;
import org.bidib.wizard.mvc.stepcontrol.model.MotorSizeType;
import org.bidib.wizard.mvc.stepcontrol.model.MovementScaleEnum;
import org.bidib.wizard.mvc.stepcontrol.model.StepControlAspect;
import org.bidib.wizard.mvc.stepcontrol.model.StepControlAspect.AspectPersistanceStatus;
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.OperationModeEnum;
import org.bidib.wizard.mvc.stepcontrol.view.AspectEditorPanel.EditorType;
import org.bidib.wizard.mvc.stepcontrol.view.excel.DataExchangeException;
import org.bidib.wizard.mvc.stepcontrol.view.excel.ExcelAspectReader;
import org.bidib.wizard.mvc.stepcontrol.view.excel.ExcelAspectReader.MotorData;
import org.bidib.wizard.mvc.stepcontrol.view.excel.ImportAspect;
import org.bidib.wizard.utils.FileUtils;
import org.oxbow.swingbits.dialog.task.TaskDialogs;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import com.jgoodies.binding.PresentationModel;
import com.jgoodies.binding.adapter.BoundedRangeAdapter;
import com.jgoodies.binding.list.IndirectListModel;
import com.jgoodies.binding.list.SelectionInList;
import com.jgoodies.binding.value.ConverterFactory;
import com.jgoodies.binding.value.ConverterValueModel;
import com.jgoodies.binding.value.ValueHolder;
import com.jgoodies.binding.value.ValueModel;
import com.jgoodies.common.base.Objects;
import com.jgoodies.forms.builder.ButtonBarBuilder;
import com.jgoodies.forms.builder.FormBuilder;
import com.jgoodies.forms.debug.FormDebugPanel;
import com.jgoodies.forms.factories.Paddings;
import com.jidesoft.grid.DefaultExpandableRow;
import com.jidesoft.grid.RowStripeTableStyleProvider;
import com.jidesoft.grid.SortableTableModel;
import com.jidesoft.grid.TablePopupMenuInstaller;
import com.jidesoft.pane.CollapsiblePane;
import com.jidesoft.swing.DefaultOverlayable;
import com.jidesoft.swing.JideBoxLayout;
import com.jidesoft.swing.JideButton;
import com.jidesoft.swing.JideSplitPane;
import com.jidesoft.swing.JideSwingUtilities;
import com.jidesoft.swing.JideToggleButton;
import com.jidesoft.swing.NullJideButton;
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, CvDefinitionPanelProvider, TabPanelProvider, PortValueListener,
AccessoryScripting {
private static final Logger LOGGER = LoggerFactory.getLogger(StepControlPanel.class);
private static final String EMERGENCY_STOP = "EMERGENCY STOP";
private static final String OPERATING = "OPERATING";
private static final String HOMING_IN_PROGRESS = "HOMING";
private static final String UNKNOWN = "UNKNOWN";
private static final String READ = "read";
private static final String WRITE = "write";
private static final long INACTIVE_POSITION_VALUE = 0xFFFFFFFFL;
private final StepControlModel stepControlModel;
private final TabStatusListener tabStatusListener;
private final FeedbackPortStatusChangeProvider feedbackPortStatusChangeProvider;
private AspectTable aspectTable;
private JPanel component;
private NodeInterface selectedNode;
private IndirectListModel aspectSelection;
private PresentationModel stepControlPresentationModel;
private ImageIcon accessoryErrorIcon;
private ImageIcon accessorySuccessfulIcon;
private ImageIcon accessoryWaitIcon;
private ImageIcon accessoryUnknownIcon;
private ImageIcon emergencyStopIcon;
private ImageIcon normalOperatingIcon;
private ImageIcon homingInProgressIcon;
private ImageIcon selectedIcon;
private ImageIcon unselectedIcon;
private ImageIcon emptyIcon;
private ImageIcon errorIcon;
private JLabel operationalStateIconLabel = new JLabel();
private JLabel executionStateIconLabel = new JLabel();
private final List cvDefinitionRequestListeners = new ArrayList<>();
private Map mapKeywordToNode;
private final List requiredConfigVariables = new ArrayList<>();
private final MainModel mainModel;
private boolean initialized;
private final StatusBar statusBar;
private final List fieldsToUpdate = new ArrayList<>();
private final List occupancyLeds = new ArrayList<>();
private JButton readCvButton;
private JButton writeCvButton;
private VLToolBar toolbarCvDefinition;
// turntable
private double degreeOffset;
private TurntableIconPanel turntableIconPanel;
private AngleRenderer angleRenderer;
private boolean showMotorSliderEditor = false;
private MotorSlider motorSliderEditor;
private MotorPort selectedMotorPort;
private StepControlControllerInterface stepControlController;
private Map functionButtonMap = new HashMap<>();
private JPanel functionButtonParentPanel;
private JSlider speedSlider;
private ValueModel speedModel;
private BoundedRangeAdapter boundedRangeAdapterSpeed;
private JSlider accelSlider;
private BoundedRangeAdapter boundedRangeAdapterAccel;
private JSlider decelSlider;
private BoundedRangeAdapter boundedRangeAdapterDecel;
private JideToggleButton soundActiveButton;
private JButton setPosition;
private JButton rotateTurnTableFlipButton;
private JButton leftStepsPosition;
private JButton leftSingleStepPosition;
private JButton rightSingleStepPosition;
private JButton rightStepsPosition;
private ScriptPanel scriptPanel;
private final SettingsService settingsService;
private final AspectExecutionModel aspectExecutionModel;
private TableModelListener aspectTableModelListener;
private JPanel detailsPanel;
private FormBuilder positionFormBuilder;
private JPanel buttonBarContainer;
public StepControlPanel(final MainModel mainModel, final StepControlModel stepControlModel,
final SettingsService settingsService, final TabStatusListener tabStatusListener,
final FeedbackPortStatusChangeProvider feedbackPortStatusChangeProvider,
StepControlControllerInterface stepControlController, final StatusBar statusBar) {
this.tabStatusListener = tabStatusListener;
this.mainModel = mainModel;
this.feedbackPortStatusChangeProvider = feedbackPortStatusChangeProvider;
this.stepControlController = stepControlController;
this.stepControlModel = stepControlModel;
this.settingsService = settingsService;
this.aspectExecutionModel = new AspectExecutionModel();
// the moving aspects are in accessory 0
this.aspectExecutionModel.setAccessoryId(0);
this.statusBar = statusBar;
}
private SortableTableModel getSortableTableModel() {
return ((SortableTableModel) aspectTable.getModel());
}
private static class DebugPanel extends FormDebugPanel implements TabSelectionListener {
private static final long serialVersionUID = 1L;
private final TabSelectionListener parent;
public DebugPanel(TabSelectionListener parent) {
this.parent = parent;
}
@Override
public void tabSelected(boolean selected) {
LOGGER.debug("Select tab, current component: is StepControlPanel.");
parent.tabSelected(selected);
}
}
/**
* Create the step control panel component.
*/
public void createComponent() {
final DefaultTabSelectionPanel container = new DefaultTabSelectionPanel(this) {
private static final long serialVersionUID = 1L;
@Override
public boolean equals(Object other) {
if (other instanceof TabComponentCreator) {
TabComponentCreator creator = (TabComponentCreator) other;
// TODO if more than a single instance is available this must be changed
if (creator.getCreator() instanceof StepControlPanel) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public void tabSelected(boolean selected) {
LOGGER.info("Select tab, current component: is StepControlPanel, selected: {}", selected);
StepControlPanel.this.tabSelected(selected);
}
};
boolean debug = false;
FormBuilder formBuilder =
FormBuilder
.create().columns("p, 3dlu, fill:p:g").rows("p, 3dlu, p")
.panel(debug ? new DebugPanel(this) : container);
formBuilder.border(Paddings.TABBED_DIALOG);
// load the icons
initializeAccessoryStateIcons();
aspectSelection =
new SelectionInList<>((ListModel) stepControlModel.getStepControlAspectsListModel());
stepControlPresentationModel = new PresentationModel<>(new ValueHolder(stepControlModel, true));
operationalStateIconLabel = new JLabel(UNKNOWN);
formBuilder.add(Resources.getString(StepControlPanel.class, "operational_state")).xy(1, 1);
formBuilder.add(operationalStateIconLabel).xy(3, 1);
operationalStateIconLabel.setIcon(accessoryUnknownIcon);
executionStateIconLabel = new JLabel();
formBuilder.add(Resources.getString(StepControlPanel.class, "execution_state")).xy(1, 3);
formBuilder.add(executionStateIconLabel).xy(3, 3);
executionStateIconLabel.setIcon(accessoryUnknownIcon);
formBuilder.appendRows("5dlu");
// create the aspect table
final JPanel overlayAspectTable = createAspectTable();
// create the 'detail panel'
this.detailsPanel = createDetailsPanel();
// create the split pane for the table and the details panel
JideSplitPane jideSplitPane = new JideSplitPane(JideSplitPane.HORIZONTAL_SPLIT);
jideSplitPane.add(overlayAspectTable, JideBoxLayout.FLEXIBLE);
jideSplitPane.add(new JScrollPane(this.detailsPanel), JideBoxLayout.FLEXIBLE);
jideSplitPane.setShowGripper(true);
formBuilder.appendRows("fill:300dlu:g");
formBuilder.add(jideSplitPane).xyw(1, 5, 3);
component = formBuilder.build();
// the name is displayed on the tab
component.setName(getName());
// 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);
switch (evt.getPropertyName()) {
case StepControlModel.PROPERTYNAME_OPERATIONAL_MODE:
switch (stepControlModel.getOperationalMode()) {
case homingInProgress:
LOGGER.info("Set the operational state label to homing in progress mode.");
operationalStateIconLabel.setIcon(homingInProgressIcon);
operationalStateIconLabel.setText(HOMING_IN_PROGRESS);
break;
case operational:
LOGGER.info("Set the operational state label to operating mode.");
operationalStateIconLabel.setIcon(normalOperatingIcon);
operationalStateIconLabel.setText(OPERATING);
break;
case emergencyStop:
LOGGER.info("Set the operational state label to emergency mode.");
operationalStateIconLabel.setIcon(emergencyStopIcon);
operationalStateIconLabel.setText(EMERGENCY_STOP);
break;
default:
LOGGER.info("Set the operational state label to unknown.");
operationalStateIconLabel.setIcon(accessoryUnknownIcon);
operationalStateIconLabel.setText(UNKNOWN);
break;
}
break;
case StepControlModel.PROPERTYNAME_SELECTED_ASPECT:
// update the selected aspect
if (stepControlModel.getSelectedAspect() == null) {
executionStateIconLabel.setToolTipText(null);
executionStateIconLabel.setIcon(null);
executionStateIconLabel.setText(UNKNOWN);
}
break;
case StepControlModel.PROPERTYNAME_CURRENT_DEGREES:
// no update of pending changes
break;
case StepControlModel.PROPERTYNAME_TARGET_DEGREES:
// no update of pending changes
break;
case StepControlModel.PROPERTYNAME_MOTOR_PORT:
// no update of pending changes
// selectedMotorPort = stepControlModel.getMotorPort();
// LOGGER.info("The selected motor port has changed, selectedMotorPort: {}", selectedMotorPort);
//
// motorSliderEditor.setPort(selectedMotorPort);
// TODO enable the motor slider when the firmware is ready
// motorSliderEditor.setEnabled(selectedMotorPort != null);
break;
case StepControlModel.PROPERTYNAME_SOUND_PORTS:
// no update of pending changes
List soundPorts = stepControlModel.getSoundPorts();
LOGGER.info("The sound ports have changed, soundPorts: {}", soundPorts);
functionButtonParentPanel.removeAll();
if (CollectionUtils.isNotEmpty(soundPorts)) {
final List functionButtons = new ArrayList<>();
createFunctionButtonPanel(functionButtonParentPanel, functionButtons);
}
functionButtonParentPanel.revalidate();
break;
case StepControlModel.PROPERTYNAME_TURNTABLE_TYPE:
turnTableTypeChanged(StepControlPanel.this.aspectTable, false);
break;
case StepControlModel.PROPERTYNAME_SOUND_ACTIVE:
// LOGGER.info("The sound active property has changed.");
if (evt.getNewValue() instanceof Boolean) {
boolean soundActive = stepControlModel.isSoundActive();
LOGGER.info("The sound active flag has changed: {}", soundActive);
if (soundActiveButton != null) {
soundActiveButton.setSelected(!soundActive);
}
}
break;
case StepControlModel.PROPERTYNAME_CURRENT_POSITION:
LOGGER.info("The current position was changed.");
break;
default:
LOGGER
.info("The property has changed: {}. This will set the pending changes marker on the tab.",
evt.getPropertyName());
tabStatusListener.updatePendingChanges(component, true);
break;
}
}
});
speedSlider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
// the slider must not fire when setting the values
if (!initialized || selectedNode == null) {
LOGGER
.info(
"The panel is not initialized or no node is selected, discard the state change of speed.");
return;
}
JSlider source = (JSlider) e.getSource();
if (!source.getValueIsAdjusting() && initialized) {
firePerformSpeed();
}
}
});
accelSlider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
// the slider must not fire when setting the values
if (!initialized || selectedNode == null) {
LOGGER
.info(
"The panel is not initialized or no node is selected, discard the state change of accel.");
return;
}
JSlider source = (JSlider) e.getSource();
if (!source.getValueIsAdjusting() && initialized) {
firePerformAccel();
}
}
});
decelSlider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
// the slider must not fire when setting the values
if (!initialized || selectedNode == null) {
LOGGER
.info(
"The panel is not initialized or no node is selected, discard the state change of decel.");
return;
}
JSlider source = (JSlider) e.getSource();
if (!source.getValueIsAdjusting() && initialized) {
firePerformDecel();
}
}
});
// motorSliderEditor.addSliderValueChangeListener(new SliderValueChangeListener() {
//
// @Override
// public void stateChanged(ChangeEvent e, boolean isAdjusting, int value) {
// // only handle if not adjusting
// if (!isAdjusting) {
// MotorPort motorPort = stepControlModel.getMotorPort();
//
// if (motorPort != null) {
// LOGGER.info("Update motor port: {}, new value: {}", motorPort.getDebugString(), value);
//
// motorPort.setValue(value);
//
// stepControlController.setMotorPortValue(motorPort);
// }
//
// }
// }
// });
toolbarCvDefinition = new VLToolBar("stepControlCvDefinition");
addToolBarButtons(toolbarCvDefinition);
addToolBar(toolbarCvDefinition, new ToolBarConstraints(0, 2));
// initially invisible
toolbarCvDefinition.setVisible(false);
feedbackPortStatusChangeProvider.addFeedbackPortListener(new DefaultFeedbackPortListener() {
@Override
public Class> getPortClass() {
return FeedbackPort.class;
}
@Override
public void statusChanged(final NodeInterface node, final FeedbackPort port) {
if (selectedNode != null && ProductUtils.isStepControl(selectedNode.getUniqueId())) {
if (port.getId() < occupancyLeds.size()) {
FeedbackPortStatus status = port.getStatus();
LOGGER.info("The status of the feedback port has changed: {}, status: {}", port, status);
Led occupancyLed = occupancyLeds.get(port.getId());
if (occupancyLed != null) {
SwingUtilities
.invokeLater(
() -> occupancyLed.setLedOn(status == FeedbackPortStatus.OCCUPIED ? true : false));
}
}
else {
LOGGER.debug("Port number of feedback port is out of range: {}", port.getId());
}
}
else {
LOGGER.info("Do not update occupancy leds because the selected node is not a StepControl.");
}
}
});
}
private JPanel createDetailsPanel() {
boolean debugDetail = false;
final FormBuilder detailFormBuilder =
FormBuilder
.create().columns("p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p:g")
.rows("p, 3dlu, p, 3dlu, p, 3dlu, p").panel(debugDetail ? new FormDebugPanel() : new JPanel());
detailFormBuilder.border(Paddings.DLU4);
// prepare the configuration wizard button
final ImageIcon iconConfigurationWizard =
ImageUtils.createImageIcon(StepControlPanel.class, "/icons/16x16/wrench.png");
JButton startConfigurationWizard =
new JButton(Resources.getString(StepControlPanel.class, "button.wizard"), iconConfigurationWizard);
startConfigurationWizard.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
LOGGER.info("Open configuration wizard.");
fireConfigurationWizard();
}
});
detailFormBuilder.add(buildLeftAlignedButtonBar(startConfigurationWizard)).xyw(1, 1, 13);
boolean debugPosition = false;
// final FormBuilder positionFormBuilder =
this.positionFormBuilder =
FormBuilder
.create().columns("max(40dlu;p), 3dlu, p, 6dlu, p, 3dlu, max(40dlu;p), 3dlu, p:g").rows("p")
.panel(debugPosition ? new FormDebugPanel() : new JPanel());
positionFormBuilder.border(Paddings.EMPTY);
final ValueModel currentPositionConverterModel =
new ConverterValueModel(
stepControlPresentationModel.getModel(StepControlModel.PROPERTYNAME_CURRENT_POSITION),
new StringToUnsignedLongConverter());
JTextField currentPosition = WizardComponentFactory.createTextField(currentPositionConverterModel);
currentPosition.setEditable(false);
positionFormBuilder.add(currentPosition).xy(1, 1);
JButton getPosition = new JButton(Resources.getString(StepControlPanel.class, "get_position"));
getPosition.setToolTipText(Resources.getString(StepControlPanel.class, "get_position.tooltip"));
getPosition.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
LOGGER.info("Get the position value.");
fireGetPosition();
}
});
positionFormBuilder.add(getPosition).xy(3, 1);
final ValueModel directPositionModel = new ValueHolder();
final ValueModel directPositionConverterModel =
new ConverterValueModel(directPositionModel, new StringToUnsignedLongConverter());
JTextField directPosition = WizardComponentFactory.createTextField(directPositionConverterModel, false);
positionFormBuilder.addLabel(Resources.getString(StepControlPanel.class, "direct")).xy(5, 1);
positionFormBuilder.add(directPosition).xy(7, 1);
// add button for setpos and getpos
setPosition = new JButton(Resources.getString(StepControlPanel.class, "set_position"));
setPosition.setToolTipText(Resources.getString(StepControlPanel.class, "set_position.tooltip"));
setPosition.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Long directPosition = (Long) directPositionModel.getValue();
if (directPosition != null) {
LOGGER.info("Set the direct position value: {}", directPosition);
fireSetDirectPosition(directPosition.intValue());
}
}
});
// rotate turntable button
ImageIcon turntableRotateFlipIcon =
ImageUtils.loadImageIcon(StepControlPanel.class, "/icons/stepcontrol/turntable-rotate-half.png", 16, 16);
rotateTurnTableFlipButton = new JButton(turntableRotateFlipIcon);
rotateTurnTableFlipButton.setToolTipText(Resources.getString(StepControlPanel.class, "rotateFlip.tooltip"));
rotateTurnTableFlipButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
LOGGER.info("Rotate turntable flip");
firePerformFlipRotate();
}
});
// Change position by steps buttons
leftStepsPosition = new JButton(Resources.getString(StepControlPanel.class, "left_steps_position"));
leftStepsPosition.setToolTipText(Resources.getString(StepControlPanel.class, "left_steps_position.tooltip"));
leftStepsPosition.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
LOGGER.info("10 steps in left position.");
Long directPosition = stepControlModel.getCurrentPosition();
if (directPosition != null) {
int newPosition = directPosition.intValue();
newPosition -= 10;
LOGGER.info("Set the position value: {}", newPosition);
fireSetDirectPosition(newPosition);
}
}
});
leftSingleStepPosition = new JButton(Resources.getString(StepControlPanel.class, "left_single_step_position"));
leftSingleStepPosition
.setToolTipText(Resources.getString(StepControlPanel.class, "left_single_step_position.tooltip"));
leftSingleStepPosition.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
LOGGER.info("Single step in left position.");
Long directPosition = stepControlModel.getCurrentPosition();
if (directPosition != null) {
int newPosition = directPosition.intValue();
newPosition -= 1;
LOGGER.info("Set the position value: {}", newPosition);
fireSetDirectPosition(newPosition);
}
}
});
rightSingleStepPosition =
new JButton(Resources.getString(StepControlPanel.class, "right_single_step_position"));
rightSingleStepPosition
.setToolTipText(Resources.getString(StepControlPanel.class, "right_single_step_position.tooltip"));
rightSingleStepPosition.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
LOGGER.info("Single step in right position.");
Long directPosition = stepControlModel.getCurrentPosition();
if (directPosition != null) {
int newPosition = directPosition.intValue();
newPosition += 1;
LOGGER.info("Set the position value: {}", newPosition);
fireSetDirectPosition(newPosition);
}
}
});
rightStepsPosition = new JButton(Resources.getString(StepControlPanel.class, "right_steps_position"));
rightStepsPosition.setToolTipText(Resources.getString(StepControlPanel.class, "right_steps_position.tooltip"));
rightStepsPosition.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
LOGGER.info("10 steps in right position.");
Long directPosition = stepControlModel.getCurrentPosition();
if (directPosition != null) {
int newPosition = directPosition.intValue();
newPosition += 10;
LOGGER.info("Set the position value: {}", newPosition);
fireSetDirectPosition(newPosition);
}
}
});
this.buttonBarContainer = new JPanel();
buildPositionsButtonBar(buttonBarContainer, positionFormBuilder);
positionFormBuilder.add(buttonBarContainer).xy(9, 1);
// final JComponent buttonBar =
// buildLeftAlignedButtonBar(setPosition, leftStepsPosition, leftSingleStepPosition, rightSingleStepPosition,
// rightStepsPosition, rotateTurnTableFlipButton);
// positionFormBuilder.add(buttonBar).xy(9, 1);
detailFormBuilder.addLabel(Resources.getString(StepControlPanel.class, "position")).xy(1, 3);
detailFormBuilder.add(positionFormBuilder.build()).xyw(3, 3, 11);
// create the speed related silders
JPanel speedSlidersPanel = createSpeedSlidersPanel();
detailFormBuilder.add(speedSlidersPanel).xyw(1, 5, 13);
// 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.addLabel(Resources.getString(StepControlPanel.class, "occupancy")).xy(1, 7);
detailFormBuilder.add(buildLeftAlignedButtonBar(occupancy1, occupancy2, occupancy3, occupancy4)).xyw(3, 7, 11);
// add the lower part with animation and sound buttons
detailFormBuilder.appendRows("3dlu, top:p:g");
// create the turntable panel
JPanel turnTablePanel = createTurnTablePanel();
detailFormBuilder.add(turnTablePanel).xyw(1, 9, 13);
return detailFormBuilder.build();
}
private JComponent buildPositionsButtonBar(final JPanel container, final FormBuilder positionFormBuilder) {
final JComponent buttonBar =
TurnTableType.pendular != stepControlModel.getTurnTableType()
? buildLeftAlignedButtonBar(container, setPosition, leftStepsPosition, leftSingleStepPosition,
rightSingleStepPosition, rightStepsPosition, rotateTurnTableFlipButton)
: buildLeftAlignedButtonBar(container, setPosition, leftStepsPosition, leftSingleStepPosition,
rightSingleStepPosition, rightStepsPosition);
return buttonBar;
}
private JPanel createSpeedSlidersPanel() {
boolean debug = false;
final FormBuilder detailFormBuilder =
FormBuilder
.create().columns("p, 3dlu, p, 3dlu, p:g:fill").rows("p, 3dlu, p, 3dlu, p")
.panel(debug ? new FormDebugPanel() : new JPanel());
detailFormBuilder.border(Paddings.EMPTY);
int row = 1;
if (showMotorSliderEditor) {
detailFormBuilder.appendRows("3dlu, p");
motorSliderEditor = new MotorSlider(-SpeedSteps.DCC128.getSteps(), SpeedSteps.DCC128.getSteps());
// create the component
// motorSliderEditor.createComponent(0);
// disable the motor slider by default
motorSliderEditor.setEnabled(false);
JPanel sliderComponent = motorSliderEditor.getComponent();
detailFormBuilder.addLabel("Motor").xy(1, row);
detailFormBuilder.add(sliderComponent).xy(3, row);
row += 2;
}
// slider for speed
speedModel = stepControlPresentationModel.getModel(StepControlModel.PROPERTYNAME_SPEED);
speedSlider = new JSlider();
speedSlider.setOpaque(false);
int maxSpeed = 2001;
int minSpeed = 1;
LOGGER.info("Use initial speed range, min: {}, max: {}", minSpeed, maxSpeed);
boundedRangeAdapterSpeed = new BoundedRangeAdapter(speedModel, 0, minSpeed, maxSpeed);
speedSlider.setModel(boundedRangeAdapterSpeed);
JLabel speedLabel =
WizardComponentFactory
.createLabel(ConverterFactory.createStringConverter(speedModel, new DecimalFormat("#####")));
speedLabel.setPreferredSize(new Dimension(40, speedLabel.getPreferredSize().height));
speedLabel.setHorizontalAlignment(SwingConstants.RIGHT);
detailFormBuilder.addLabel(Resources.getString(StepControlPanel.class, "speed")).xy(1, row);
detailFormBuilder.add(speedLabel).xy(3, row);
detailFormBuilder.add(speedSlider).xy(5, row);
row += 2;
// slider for accel
ValueModel accelModel = stepControlPresentationModel.getModel(StepControlModel.PROPERTYNAME_ACCEL);
accelSlider = new JSlider() {
private static final long serialVersionUID = 1L;
@Override
public void setValueIsAdjusting(boolean b) {
try {
super.setValueIsAdjusting(b);
}
catch (Exception ex) {
LOGGER.warn("Set the new value failed.", ex);
// JOptionPane.showConfirmDialog(detailFormBuilder.getPanel(), "set accel failed.");
showErrorDialog(detailFormBuilder.getPanel(),
Resources.getString(StepControlPanel.class, "adjust-accel-value-failed.title"),
Resources.getString(StepControlPanel.class, "adjust-accel-value-failed.instruction"),
Resources.getString(StepControlPanel.class, "adjust-accel-value-failed.text"), ex);
}
}
@Override
public void setValue(int n) {
try {
super.setValue(n);
}
catch (Exception ex) {
LOGGER.warn("Set the new value failed.", ex);
// JOptionPane.showConfirmDialog(detailFormBuilder.getPanel(), "set accel failed.");
showErrorDialog(detailFormBuilder.getPanel(),
Resources.getString(StepControlPanel.class, "adjust-accel-value-failed.title"),
Resources.getString(StepControlPanel.class, "adjust-accel-value-failed.instruction"),
Resources.getString(StepControlPanel.class, "adjust-accel-value-failed.text"), ex);
}
}
private void showErrorDialog(
JComponent component, String title, String instruction, String text, Exception ex) {
TaskDialogs
.build(JOptionPane.getFrameForComponent(component), instruction, text).title(title)
.showException(ex);
}
};
boundedRangeAdapterAccel = new BoundedRangeAdapter(accelModel, 1, 1, 65536);
accelSlider.setModel(boundedRangeAdapterAccel);
accelSlider.setOpaque(false);
JLabel accelLabel =
WizardComponentFactory
.createLabel(ConverterFactory.createStringConverter(accelModel, new DecimalFormat("#####")));
accelLabel.setHorizontalAlignment(SwingConstants.RIGHT);
detailFormBuilder.addLabel(Resources.getString(StepControlPanel.class, "accel")).xy(1, row);
detailFormBuilder.add(accelLabel).xy(3, row);
detailFormBuilder.add(accelSlider).xy(5, row);
row += 2;
// slider for decel
ValueModel decelModel = stepControlPresentationModel.getModel(StepControlModel.PROPERTYNAME_DECEL);
decelSlider = new JSlider() {
private static final long serialVersionUID = 1L;
@Override
public void setValueIsAdjusting(boolean b) {
try {
super.setValueIsAdjusting(b);
}
catch (Exception ex) {
LOGGER.warn("Set the new value failed.", ex);
// JOptionPane.showConfirmDialog(detailFormBuilder.getPanel(), "set decel failed.");
showErrorDialog(detailFormBuilder.getPanel(),
Resources.getString(StepControlPanel.class, "adjust-decel-value-failed.title"),
Resources.getString(StepControlPanel.class, "adjust-decel-value-failed.instruction"),
Resources.getString(StepControlPanel.class, "adjust-decel-value-failed.text"), ex);
}
}
@Override
public void setValue(int n) {
try {
super.setValue(n);
}
catch (Exception ex) {
LOGGER.warn("Set the new value failed.", ex);
// JOptionPane.showConfirmDialog(detailFormBuilder.getPanel(), "set decel failed.");
showErrorDialog(detailFormBuilder.getPanel(),
Resources.getString(StepControlPanel.class, "adjust-decel-value-failed.title"),
Resources.getString(StepControlPanel.class, "adjust-decel-value-failed.instruction"),
Resources.getString(StepControlPanel.class, "adjust-decel-value-failed.text"), ex);
}
}
private void showErrorDialog(
JComponent component, String title, String instruction, String text, Exception ex) {
TaskDialogs
.build(JOptionPane.getFrameForComponent(component), instruction, text).title(title)
.showException(ex);
}
};
boundedRangeAdapterDecel = new BoundedRangeAdapter(decelModel, 1, 1, 65536);
decelSlider.setModel(boundedRangeAdapterDecel);
decelSlider.setOpaque(false);
JLabel decelLabel =
WizardComponentFactory
.createLabel(ConverterFactory.createStringConverter(decelModel, new DecimalFormat("#####")));
decelLabel.setHorizontalAlignment(SwingConstants.RIGHT);
detailFormBuilder.addLabel(Resources.getString(StepControlPanel.class, "decel")).xy(1, row);
detailFormBuilder.add(decelLabel).xy(3, row);
detailFormBuilder.add(decelSlider).xy(5, row);
JPanel speedSliderPanel = detailFormBuilder.build();
CollapsiblePane pane =
new CollapsiblePane(Resources.getString(StepControlPanel.class, "pane-speed-and-accelaration"));
final ImageIcon speedAndAccelarationIcon =
ImageUtils.createImageIcon(StepControlPanel.class, "/icons/16x16/wrench.png");
pane.setIcon(speedAndAccelarationIcon);
pane.setContentPane(JideSwingUtilities.createTopPanel(speedSliderPanel));
try {
pane.setCollapsed(true);
}
catch (PropertyVetoException ex) {
LOGGER.warn("Collapse pane was vetoed.", ex);
}
return pane;
}
private JPanel createTurnTablePanel() {
boolean debugTTFB = false;
final FormBuilder turntableFormBuilder =
FormBuilder
.create().columns("p, 3dlu, p, 10dlu, p:g").rows("top:p:g")
.panel(debugTTFB ? new FormDebugPanel() : new JPanel());
// add the turntable image
turntableIconPanel = prepareTurntableIconPanel();
// create a panel with the turntable animation
JPanel contentPanel = new JPanel(new BorderLayout());
contentPanel.setMinimumSize(new Dimension(140, 140));
contentPanel.setPreferredSize(new Dimension(140, 140));
contentPanel.setOpaque(true);
contentPanel.add(turntableIconPanel, BorderLayout.CENTER);
turntableFormBuilder.add(contentPanel).xy(1, 1);
// create the panel for the sound function buttons
functionButtonParentPanel = new JPanel();
turntableFormBuilder.add(functionButtonParentPanel).xy(3, 1);
if (settingsService.getWizardSettings().isPowerUser()) {
// prepare the script panel
scriptPanel = new ScriptPanel(this, settingsService);
JPanel panel = scriptPanel.createPanel();
panel
.setBorder(BorderFactory
.createTitledBorder(BorderFactory.createEtchedBorder(),
Resources.getString(getClass(), "script") + ":"));
turntableFormBuilder.add(panel).xy(5, 1);
}
else {
LOGGER.info("The script panel for StepControl is skipped because the user is not a power user.");
}
return turntableFormBuilder.build();
}
private TurntableIconPanel prepareTurntableIconPanel() {
final ExperimentalSettingsInterface experimentalSettings = this.settingsService.getExperimentalSettings();
experimentalSettings
.addPropertyChangeListener(ExperimentalSettingsInterface.PROPERTY_TURNTABLE_ANGLE_OFFSET, evt -> {
this.degreeOffset = experimentalSettings.getTurntableAngleOffset();
double currentDegrees = stepControlModel.getTurntableCurrentDegrees();
setTurntableDegrees(currentDegrees);
});
// initial offset
this.degreeOffset = experimentalSettings.getTurntableAngleOffset();
ImageIcon turntableBackgroundIcon =
ImageUtils.loadImageIcon(StepControlPanel.class, "/icons/stepcontrol/turntable-bg.png", 120, 120);
ImageIcon turntableBackgroundLinearIcon =
ImageUtils.loadImageIcon(StepControlPanel.class, "/icons/stepcontrol/turntable-bg-linear.png", 120, 120);
ImageIcon turntableBackgroundPendularIcon =
ImageUtils.loadImageIcon(StepControlPanel.class, "/icons/stepcontrol/turntable-bg-pendular.png", 120, 120);
ImageIcon turntableBasicIcon =
ImageUtils.loadImageIcon(StepControlPanel.class, "/icons/stepcontrol/turntable-platform.png", 110, 110);
ImageIcon turntablePendularIcon =
ImageUtils.loadImageIcon(StepControlPanel.class, "/icons/stepcontrol/turntable-cableways.png", 40, 60);
TurntableIconPanel turntableIconPanel =
new TurntableIconPanel(turntableBackgroundIcon, turntableBackgroundLinearIcon,
turntableBackgroundPendularIcon, turntableBasicIcon, turntablePendularIcon, degreeOffset,
TurnTableType.unknown);
turntableIconPanel.prepareComponent();
turntableIconPanel.setMinimumSize(new Dimension(140, 140));
turntableIconPanel.setPreferredSize(new Dimension(140, 140));
turntableIconPanel.setBounds(0, 0, 140, 140);
return turntableIconPanel;
}
private void turnTableTypeChanged(final AspectTable aspectTable, boolean initial) {
final TurnTableType turnTableType = stepControlModel.getTurnTableType();
LOGGER.info("The turntable type has changed: {}", turnTableType);
// refresh the aspects
aspectTable.turnTableTypeChanged(turnTableType);
if (this.turntableIconPanel != null) {
this.turntableIconPanel.setTurnTableType(turnTableType);
}
if (this.rotateTurnTableFlipButton != null) {
this.buttonBarContainer.removeAll();
buildPositionsButtonBar(this.buttonBarContainer, positionFormBuilder);
this.detailsPanel.updateUI();
}
if (!initial && selectedNode != null) {
int maxConfiguredAspects = StepControlControllerInterface.MAX_CONFIGURED_ASPECTS;
switch (turnTableType) {
case round:
maxConfiguredAspects = StepControlControllerInterface.MAX_CONFIGURED_ASPECTS / 2;
break;
case pendular:
maxConfiguredAspects = 2;
break;
default:
break;
}
stepControlModel.setMaxConfiguredAspects(maxConfiguredAspects);
LOGGER
.info("The turnTableType was set to: {}, maxConfiguredAspects: {}", turnTableType,
maxConfiguredAspects);
final Map cvNumberToNodeMap = getCvNumberToNodeMap(selectedNode);
final AtomicBoolean errorDetected = new AtomicBoolean();
// create the list of aspects with the values from the config variables
final List configuredAspects =
prepareConfiguredAspects(cvNumberToNodeMap, errorDetected);
// set the aspects silently
try {
aspectTable.getModel().removeTableModelListener(aspectTableModelListener);
stepControlModel.setStepControlAspects(configuredAspects);
}
finally {
aspectTable.getModel().addTableModelListener(aspectTableModelListener);
}
this.aspectTable.expandAllRows();
}
}
/**
* Create the aspect table
*/
private JPanel createAspectTable() {
final JPanel contentPanel = new JPanel();
contentPanel.setLayout(new BorderLayout());
final AspectTableModel tableModel =
new AspectTableModel(aspectSelection, new String[] { //
Resources.getString(StepControlPanel.class, "aspect"), //
Resources.getString(StepControlPanel.class, "position"), //
Resources.getString(StepControlPanel.class, "angle"), //
Resources.getString(StepControlPanel.class, "polarity") //
}, this.stepControlModel);
// this is the callback listener for the update of aspect
final AspectCallbackListener aspectCallbackListener = new AddAspectCallbackListener();
// prepare the icons
selectedIcon = ImageUtils.createImageIcon(StepControlPanel.class, "/icons/stepcontrol/arrow_straight.png");
unselectedIcon = ImageUtils.createImageIcon(StepControlPanel.class, "/icons/stepcontrol/arrow_switch.png");
// create the table
this.aspectTable =
new AspectTable(tableModel, Resources.getString(StepControlPanel.class, "emptyTable"), selectedIcon,
unselectedIcon, aspectCallbackListener);
this.aspectTable
.setTableStyleProvider(new RowStripeTableStyleProvider(UIManager.getColor("tableRowStripe.background"),
UIManager.getColor("tableRowStripe.alternativeBackground")));
this.aspectTable.setName("Aspect Table");
// aspectTable.setShowGrid(false);
this.aspectTable.setRowHeight(24);
this.aspectTable.setSortingEnabled(false);
// sort the table automatically
this.aspectTable.setAutoResort(true);
this.aspectTable.sortColumn(AspectTableModel.COLUMN_POSITION);
this.aspectTable.setShowSortOrderNumber(false);
this.aspectTable.setRestoreSelectionAndRowHeightAutomatically(true);
this.aspectTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
this.aspectTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
int row = aspectTable.getSelectedRow();
if (row != -1) {
aspectTable.expandRow(row);
}
}
});
this.aspectTable.createComponentFactory(stepControlModel);
this.aspectTable.getColumnModel().getColumn(AspectTableModel.COLUMN_ASPECT).setPreferredWidth(80);
this.aspectTable.getColumnModel().getColumn(AspectTableModel.COLUMN_ASPECT).setMaxWidth(80);
// create a renderer for the aspect column that renders the correct row number
this.aspectTable
.getColumnModel().getColumn(AspectTableModel.COLUMN_ASPECT)
.setCellRenderer(new AspectCellRenderer(this.stepControlModel));
this.aspectTable.getColumnModel().getColumn(AspectTableModel.COLUMN_POSITION).setPreferredWidth(100);
this.aspectTable
.getColumnModel().getColumn(AspectTableModel.COLUMN_POSITION)
.setCellRenderer(new StepControlAspectCellRenderer(stepControlModel, emptyIcon, errorIcon));
// this.aspectTable.setPreferredScrollableViewportSize(new Dimension(500, 200));
this.aspectTable.setFillsViewportHeight(true);
this.aspectTable.setFillsViewportWithStripe(false);
final JScrollPane scrollPane = new JScrollPane(this.aspectTable);
final DefaultOverlayable overlayAspectTable = new DefaultOverlayable(scrollPane);
this.aspectTable.getModel().addTableModelListener(new TableModelListener() {
@Override
public void tableChanged(TableModelEvent e) {
// set the overlay for empty table visible
overlayAspectTable.setOverlayVisible(aspectTable.getModel().getRowCount() == 0);
}
});
final NullJideButton createAspectLinkButton = new NullJideButton(this.aspectTable.getEmptyTableText());
createAspectLinkButton.setButtonStyle(NullJideButton.HYPERLINK_STYLE);
createAspectLinkButton.addActionListener(evt -> {
fireCreateNewAspect();
});
overlayAspectTable.addOverlayComponent(createAspectLinkButton);
contentPanel.add(overlayAspectTable, BorderLayout.CENTER);
final TablePopupMenuInstaller installer = new TablePopupMenuInstaller(this.aspectTable) {
@Override
protected JPopupMenu createPopupMenu() {
LOGGER.info("Create the popup menu.");
return new AspectTablePopupMenu();
}
@Override
protected void customizeMenuItems(JTable table, JPopupMenu popup, int clickingRow, int clickingColumn) {
LOGGER.info("Customize the popup menu: {}", popup);
if (clickingRow > -1 && table.getSelectedRow() != clickingRow) {
// check if the current selected row is dirty
int selectedRow = aspectTable.getSelectedRow();
if (selectedRow > -1) {
if (!aspectTable.checkAspectEditorIsDirty(selectedRow)) {
LOGGER.info("Check if aspect editor is dirty failed. Do not show the add aspect editor.");
// remove all items to prevent show
popup.removeAll();
return;
}
}
table.setRowSelectionInterval(clickingRow, clickingRow);
}
super.customizeMenuItems(table, popup, clickingRow, clickingColumn);
}
};
// force refresh the aspects
turnTableTypeChanged(this.aspectTable, true);
aspectSelection.addListDataListener(new ListDataListener() {
@Override
public void intervalRemoved(ListDataEvent e) {
LOGGER.info("intervalRemoved, e: {}", e);
tableModel.fireTableDataChanged();
}
@Override
public void intervalAdded(ListDataEvent e) {
LOGGER.info("intervalAdded");
tableModel.fireTableDataChanged();
}
@Override
public void contentsChanged(ListDataEvent e) {
LOGGER.info("contentsChanged, resort the table.");
aspectTable.resort();
LOGGER.info("contentsChanged, scroll to top.");
aspectTable.scrollToTop();
}
});
return contentPanel;
}
protected void firePerformFlipRotate() {
LOGGER.info("Rotate the turntable half.");
fireGetPosition();
Long value = stepControlModel.getCurrentPosition();
if (value == null) {
LOGGER.warn("The position value is null.");
return;
}
Long totalSteps = stepControlModel.getTotalSteps();
if (totalSteps == null) {
LOGGER.warn("The total steps value is null.");
return;
}
int currentPosition = value.intValue();
// check if the current position is on an aspect and if so use the paired aspect as target
if (stepControlModel.getSelectedAspect() != null
&& stepControlModel.getSelectedAspect().getPosition() == currentPosition) {
AspectReference oppositeAspect = stepControlModel.getSelectedAspect().getOppositeAspect();
LOGGER.info("Flip turntable to opposite aspect: {}", oppositeAspect);
firePerformAspect((StepControlAspect) oppositeAspect);
return;
}
long targetPosition = currentPosition + totalSteps / 2;
if (targetPosition >= totalSteps) {
targetPosition -= totalSteps;
}
LOGGER.info("Calculated target position: {}", targetPosition);
try {
moveToTargetPosition((int) targetPosition);
}
catch (PositionOutOfRangeException ex) {
LOGGER.info("Set direct position failed: {}", targetPosition);
JOptionPane
.showMessageDialog(component,
Resources.getString(StepControlPanel.class, "set_position_failed_message", targetPosition),
Resources.getString(StepControlPanel.class, "set_position_title"), JOptionPane.ERROR_MESSAGE);
}
}
protected void fireGetPosition() {
LOGGER.info("Read the current position value.");
try {
final List cvList = new ArrayList<>();
final Function, List> prepareFunction =
(cvList1) -> prepareGetCurrentPosition(cvList1);
final Function, List> loadFunction =
(cvList1) -> fireLoadConfigVariablesPosition(cvList1);
final Function, List> prepareAndLoad =
prepareFunction.andThen(loadFunction);
List result = prepareAndLoad.apply(cvList);
LOGGER.info("Fetched the current position: {}", result);
Long currentPosition = Long.parseLong(result.get(0).getValue());
stepControlModel.setCurrentPosition(currentPosition);
}
catch (Exception ex) {
LOGGER.warn("Get the new {} value failed.", StepControlKeywords.CVKEY_CURRENT_POSITION, ex);
}
}
private List prepareGetCurrentPosition(final List cvList) {
LOGGER.info("prepareGetCurrentPosition, current items in fieldsToUpdate: {}", fieldsToUpdate);
fieldsToUpdate.clear();
// add the current position
final ConfigurationVariable cv = new ConfigurationVariable(StepControlKeywords.CVKEY_CURRENT_POSITION, null);
addConfigVariableDistinct(cvList, cv);
final Map cvNumberToNodeMap = getCvNumberToNodeMap(selectedNode);
CvNode cvNode = cvNumberToNodeMap.get(StepControlKeywords.CVKEY_CURRENT_POSITION);
if (cvNode == null) {
CVType cvType = new CVType();
cvType.setNumber(StepControlKeywords.CVKEY_CURRENT_POSITION);
cvType.setType(DataType.STRING);
cvNode = new CvNode(cvType, cv);
LOGGER.info("Add the transient CvNode for current position: {}", cvNode);
cvNumberToNodeMap.put(StepControlKeywords.CVKEY_CURRENT_POSITION, cvNode);
}
return cvList;
}
protected void fireSetDirectPosition(Integer directPosition) {
if (directPosition == null) {
LOGGER.warn("The position value is null.");
return;
}
int currentPosition = directPosition.intValue();
try {
moveToTargetPosition(currentPosition);
}
catch (PositionOutOfRangeException ex) {
LOGGER.info("Set direct position failed: {}", currentPosition);
JOptionPane
.showMessageDialog(component,
Resources.getString(StepControlPanel.class, "set_position_failed_message", currentPosition),
Resources.getString(StepControlPanel.class, "set_position_title"), JOptionPane.ERROR_MESSAGE);
}
}
/**
* Move the turntable to the provided position.
*
* @param targetPosition
* the new position
* @throws PositionOutOfRangeException
*/
protected void moveToTargetPosition(int targetPosition) throws PositionOutOfRangeException {
LOGGER.info("Write the new target position value: {}", targetPosition);
int maxPosition = 0;
Long totalSteps = stepControlModel.getTotalSteps();
if (totalSteps != null) {
maxPosition = totalSteps.intValue();
}
if (targetPosition < 0 || targetPosition > maxPosition) {
LOGGER.warn("Position is outside range: {}", targetPosition);
throw new PositionOutOfRangeException(targetPosition, 0, maxPosition - 1, -1);
}
try {
final List cvList = new ArrayList<>();
// add the current position
ConfigurationVariable cv =
new ConfigurationVariable(StepControlKeywords.CVKEY_CURRENT_POSITION, Integer.toString(targetPosition));
cvList.add(cv);
final Map cvNumberToNodeMap = getCvNumberToNodeMap(selectedNode);
CvNode cvNode = cvNumberToNodeMap.get(StepControlKeywords.CVKEY_CURRENT_POSITION);
if (cvNode == null) {
CVType cvType = new CVType();
cvType.setNumber(StepControlKeywords.CVKEY_CURRENT_POSITION);
cvType.setType(DataType.STRING);
cvNode = new CvNode(cvType, cv);
LOGGER.info("Add the transient CvNode for current position: {}", cvNode);
cvNumberToNodeMap.put(StepControlKeywords.CVKEY_CURRENT_POSITION, cvNode);
}
else {
cvNode.setNewValue(Integer.toString(targetPosition));
}
// write the config variables to the node
fireWriteConfigVariablesPosition(cvList);
}
catch (Exception ex) {
LOGGER.warn("Set the new {} value failed.", StepControlKeywords.CVKEY_CURRENT_POSITION, ex);
}
}
public void setTurntableDegrees(double degree) {
LOGGER.info("Set degree: {}", degree);
if (turntableIconPanel != null) {
degree = degree + this.degreeOffset;
if (degree > 359) {
degree = degree - 360;
}
turntableIconPanel.setDegrees(degree);
turntableIconPanel.repaintTurntable();
}
}
/**
* 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");
emergencyStopIcon =
ImageUtils.createImageIcon(StepControlPanel.class, "/icons/stepcontrol/emergency-stop.png", 16, 16);
normalOperatingIcon = ImageUtils.createImageIcon(StepControlPanel.class, "/icons/green-leaf.png");
homingInProgressIcon = ImageUtils.createImageIcon(StepControlPanel.class, "/icons/red-leaf.png");
emptyIcon = ImageUtils.createImageIcon(StepControlPanel.class, "/icons/empty.png");
errorIcon = ImageUtils.createImageIcon(StepControlPanel.class, "/icons/error-leaf.png");
}
private void firePerformAspect(final StepControlAspect stepControlAspect) {
LOGGER.info("Aspect is performed on stepControlAspect: {}", stepControlAspect);
int aspectNumber = -1;
// get the aspect number from the position of the stepControl aspect
for (int rowIndex = 0; rowIndex < aspectTable.getRowCount(); rowIndex++) {
StepControlAspect currentAspect =
(StepControlAspect) getSortableTableModel().getValueAt(rowIndex, AspectTableModel.COLUMN_POSITION);
if (Objects.equals(currentAspect.getPosition(), stepControlAspect.getPosition())) {
aspectNumber = rowIndex;
LOGGER.info("Found the aspect to activate at row: {}, aspectNumber: {}", rowIndex, aspectNumber);
break;
}
}
firePerformAspect(aspectNumber);
}
private void firePerformOppositeAspect(final StepControlAspect stepControlAspect) {
LOGGER.info("Opposite aspect is performed on stepControlAspect: {}", stepControlAspect);
int aspectNumber = -1;
// get the aspect number from the position of the stepControl aspect
int totalRows = aspectTable.getRowCount();
for (int rowIndex = 0; rowIndex < totalRows; rowIndex++) {
StepControlAspect currentAspect =
(StepControlAspect) getSortableTableModel().getValueAt(rowIndex, AspectTableModel.COLUMN_POSITION);
if (Objects.equals(currentAspect.getOppositePosition(), stepControlAspect.getOppositePosition())) {
aspectNumber = rowIndex + totalRows;
LOGGER
.info("Found the opposite aspect to activate at row: {}, aspectNumber: {}", rowIndex, aspectNumber);
break;
}
}
firePerformAspect(aspectNumber);
}
private void firePerformAspect(int aspectNumber) {
LOGGER.info("The controlling aspect is performed, current aspectNumber: {}", aspectNumber);
final Accessory accessory = new Accessory();
accessory.setId(StepControlControllerInterface.ACCESSORY_ID_CONTROLLING);
if (aspectNumber < 0 || aspectNumber > StepControlControllerInterface.MAX_CONFIGURED_ASPECTS) {
LOGGER
.warn("The current aspect has no valid aspectNumber assigned (0..{}). Abort perform aspect: {}",
StepControlControllerInterface.MAX_CONFIGURED_ASPECTS, aspectNumber);
return;
}
stepControlController.activateAspect(accessory, aspectNumber);
// update the aspectId in the aspect execution model
synchronized (aspectExecutionModel) {
aspectExecutionModel.setAspectId(aspectNumber);
}
}
private void firePerformSpeed() {
int speed = stepControlModel.getSpeed();
LOGGER.info("Write the speed value: {}", speed);
writeCvValue(StepControlKeywords.KEYWORD_SPEED, speed);
}
private void firePerformAccel() {
int accel = stepControlModel.getAccel();
writeCvValue(StepControlKeywords.KEYWORD_ACCEL, accel);
}
private void firePerformDecel() {
int decel = stepControlModel.getDecel();
writeCvValue(StepControlKeywords.KEYWORD_DECEL, decel);
}
private void writeCvValue(String keyword, int modelValue) {
try {
final List cvList = new ArrayList<>();
// check the value
CvNode speedNode = StepControlCvUtils.getNode(mapKeywordToNode, keyword);
CvValueUtils
.compareAndAddNewValue(speedNode, Integer.toString(modelValue), cvList,
getCvNumberToNodeMap(selectedNode));
LOGGER.info("Write CV value, keyword: {}, modelValue: {}, cvList: {}", keyword, modelValue, cvList);
if (CollectionUtils.isNotEmpty(cvList)) {
// write the config variables to the node
fireWriteConfigVariables(cvList);
}
else {
LOGGER.warn("No CV values to write found.");
}
}
catch (Exception ex) {
LOGGER.warn("Set the new {} value failed.", keyword, ex);
}
}
private void readCurrentValuesFromCV(final NodeInterface 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);
if (mapKeywordToNode != null) {
mapKeywordToNode.clear();
}
return;
}
LOGGER.info("Get the cvDefinitionTreeTableModel from the node: {}", node);
CvDefinitionPanelController cvDefinitionPanelController =
DefaultApplicationContext
.getInstance()
.get(DefaultApplicationContext.KEY_CVDEFINITIONPANEL_CONTROLLER, CvDefinitionPanelController.class);
CvDefinitionTreeTableModel cvDefinitionTreeTableModel =
cvDefinitionPanelController.getCvDefinitionTreeTableModel(node);
LOGGER.info("Current cvDefinitionTreeTableModel: {}", cvDefinitionTreeTableModel);
if (cvDefinitionTreeTableModel != null) {
// search the keywords
mapKeywordToNode = new LinkedHashMap<>();
DefaultExpandableRow rootNode = (DefaultExpandableRow) cvDefinitionTreeTableModel.getRoot();
if (rootNode != null) {
CvNodeUtils.harvestKeywordNodes(rootNode, mapKeywordToNode);
}
LOGGER.info("Found keywords in nodes: {}", mapKeywordToNode.keySet());
// load the speed range
CvNode cvNodeSpeed = StepControlCvUtils.getNode(mapKeywordToNode, StepControlKeywords.KEYWORD_SPEED);
int maxSpeed = 2001;
int minSpeed = 1;
if (cvNodeSpeed != null) {
try {
maxSpeed = Integer.parseInt(cvNodeSpeed.getCV().getMax()) + 1;
}
catch (Exception ex) {
LOGGER.warn("Get the max speed from CV definition failed.", ex);
}
try {
minSpeed = Integer.parseInt(cvNodeSpeed.getCV().getMin());
}
catch (Exception ex) {
LOGGER.warn("Get the min speed from CV definition failed.", ex);
}
}
boundedRangeAdapterSpeed.setMinimum(minSpeed);
boundedRangeAdapterSpeed.setMaximum(maxSpeed);
}
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)) {
final List configVariables = new ArrayList<>();
final Map cvNumberToNodeMap = getCvNumberToNodeMap(selectedNode);
for (CvNode cvNode : mapKeywordToNode.values()) {
// LOGGER.info("Process cvNode: {}", cvNode);
prepareConfigVariables(cvNode, configVariables, cvNumberToNodeMap);
}
// keep the list of config variables
this.requiredConfigVariables.clear();
if (CollectionUtils.isNotEmpty(configVariables)) {
this.requiredConfigVariables.addAll(configVariables);
}
// add the current position with the special CV
prepareGetCurrentPosition(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();
addConfigVariableDistinct(configVariables, masterNode.getConfigVar());
for (CvNode slaveNode : masterNode.getSlaveNodes()) {
addConfigVariableDistinct(configVariables, slaveNode.getConfigVar());
}
break;
case INT:
// INT nodes are processed
addConfigVariableDistinct(configVariables, cvNode.getConfigVar());
int highCvNum = Integer.parseInt(cvNode.getCV().getHigh());
int cvNumber = Integer.parseInt(cvNode.getCV().getNumber());
if (highCvNum == cvNumber) {
// search the low CV
CvNode lowCvNode = cvNumberToNodeMap.get(cvNode.getCV().getLow());
addConfigVariableDistinct(configVariables, lowCvNode.getConfigVar());
}
else {
// search the high CV
CvNode highCvNode = cvNumberToNodeMap.get(cvNode.getCV().getHigh());
if (highCvNode == null) {
LOGGER.warn("The highCvNode is not available: {}", cvNode.getCV());
}
addConfigVariableDistinct(configVariables, highCvNode.getConfigVar());
}
break;
default:
addConfigVariableDistinct(configVariables, cvNode.getConfigVar());
break;
}
}
catch (Exception ex) {
LOGGER.warn("Prepare config variables to read from node failed.", ex);
}
}
/**
* Add the config variable to the list if it is not in list already.
*
* @param configVariables
* the list of config variables
*/
private void addConfigVariableDistinct(
final List configVariables, final ConfigurationVariable cv) {
if (!configVariables.contains(cv)) {
configVariables.add(cv);
}
else {
LOGGER.info("Skip add CV because it's in the list already: {}", cv);
}
}
public void addCvDefinitionRequestListener(CvDefinitionRequestListener l) {
cvDefinitionRequestListeners.add(l);
}
private List fireLoadConfigVariables(List configVariables) {
// TODO decouple the AWT-thread from this work?
LOGGER.info("Load the config variables.");
for (CvDefinitionRequestListener l : cvDefinitionRequestListeners) {
return l.loadCvValues(configVariables);
}
return Collections.emptyList();
}
private JComponent buildLeftAlignedButtonBar(JPanel container, JComponent... button) {
return new ButtonBarBuilder(container).addButton(button).addGlue().build();
}
private JComponent buildLeftAlignedButtonBar(JComponent... button) {
return new ButtonBarBuilder().addButton(button).addGlue().build();
}
@Override
public JPanel getComponent() {
return component;
}
public String getName() {
return Resources.getString(getClass(), "name");
}
private final AtomicBoolean cvValuesLoaded = new AtomicBoolean();
@Override
public void tabSelected(boolean selected) {
LOGGER.info("Tab is selected: {}, initialized: {}", selected, initialized);
// show the toolbar
toolbarCvDefinition.setVisible(selected);
// if the tab is selected and the initialized flag is false we must load the CV definitions
if (selected) {
triggerLoadCvValues();
}
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;
LOGGER.info("Clear the mode and set operational mode to unknown.");
stepControlModel.setOperationalMode(OperationModeEnum.unknown);
}
}
public void triggerLoadCvValues() {
final NodeInterface node = selectedNode;
LOGGER
.info("Load the CV values for node: {}, initialized: {}, cvValuesLoaded: {}", node, initialized,
cvValuesLoaded);
if (!cvValuesLoaded.get() && !initialized && selectedNode != null) {
LOGGER.info("The CV values will be read from node.");
statusBar.setStatusText(Resources.getString(StepControlPanel.class, "loading_cvvalues"));
cvValuesLoaded.set(true);
// call this method after node was selected
SwingUtilities.invokeLater(() -> readCurrentValuesFromCV(node));
}
else {
LOGGER.info("Skip loading CV values because the initialized is true.");
}
}
private final class AspectTablePopupMenu extends BasicPopupMenu {
private static final long serialVersionUID = 1L;
private JMenuItem newLabel;
private JMenuItem deleteLabel;
private JMenuItem deleteAllLabel;
private JMenuItem importAspectsFromExcelMenuItem;
private JMenuItem exportAspectsToExcelMenuItem;
public AspectTablePopupMenu() {
newLabel = new JMenuItem(Resources.getString(StepControlPanel.class, "newAspect") + " ...");
newLabel.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
LOGGER.info("Add new aspect.");
fireCreateNewAspect();
}
});
add(newLabel);
deleteLabel = new JMenuItem(Resources.getString(StepControlPanel.class, "deleteAspect") + " ...");
deleteLabel.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fireRemoveAspect();
}
});
add(deleteLabel);
deleteAllLabel = new JMenuItem(Resources.getString(StepControlPanel.class, "deleteAllAspects") + " ...");
deleteAllLabel.setToolTipText(Resources.getString(StepControlPanel.class, "deleteAllAspects.tooltip"));
deleteAllLabel.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fireRemoveAllAspects();
}
});
add(deleteAllLabel);
addSeparator();
// import aspects from Excel file
importAspectsFromExcelMenuItem =
new JMenuItem(Resources.getString(StepControlPanel.class, "importAspectsFromExcel") + " ...");
importAspectsFromExcelMenuItem
.setToolTipText(Resources.getString(StepControlPanel.class, "importAspectsFromExcel.tooltip"));
importAspectsFromExcelMenuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fireImportAspectsFromExcel();
}
});
add(importAspectsFromExcelMenuItem);
// export aspects to Excel file
exportAspectsToExcelMenuItem =
new JMenuItem(Resources.getString(StepControlPanel.class, "exportAspectsToExcel") + " ...");
exportAspectsToExcelMenuItem
.setToolTipText(Resources.getString(StepControlPanel.class, "exportAspectsToExcel.tooltip"));
exportAspectsToExcelMenuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fireExportAspectsToExcel();
}
});
add(exportAspectsToExcelMenuItem);
}
public void setDeleteEnabled(boolean enabled) {
deleteLabel.setEnabled(enabled);
}
public void setDeleteAllEnabled(boolean enabled) {
deleteAllLabel.setEnabled(enabled);
}
}
private final class AddAspectDialog extends EscapeDialog {
private static final long serialVersionUID = 1L;
private int result = JOptionPane.CANCEL_OPTION;
private final AspectEditorPanel aspectEditorPanel;
public AddAspectDialog(Frame parent, String title, boolean modal, final AspectEditorPanel aspectEditorPanel) {
super(parent, title, modal);
this.aspectEditorPanel = aspectEditorPanel;
getContentPane().setLayout(new BorderLayout());
getContentPane().add(aspectEditorPanel);
aspectEditorPanel.setDefaultButton();
pack();
setLocationRelativeTo(parent);
setMinimumSize(getSize());
// setVisible(true);
};
protected void setResult(int result) {
this.result = result;
}
public int getResult() {
return result;
}
public void cleanup() {
if (aspectEditorPanel != null) {
aspectEditorPanel.cleanup();
}
}
}
private final class AddAspectCallbackListener implements AspectCallbackListener {
private AddAspectDialog aspectDialog;
private void setAddAspectDialog(final AddAspectDialog aspectDialog) {
LOGGER.info("Set the aspect dialog: {}", aspectDialog);
this.aspectDialog = aspectDialog;
}
@Override
public void activateAspect(final StepControlAspect stepControlAspect) {
LOGGER.info("Activate the aspect is called for new aspect: {}", stepControlAspect);
// make sure the aspect is valid and saved
if (stepControlAspect.isValid() && (stepControlModel.getTurnTableType() == TurnTableType.round
? stepControlAspect.isOppositeValid() : true)
&& stepControlAspect.getStatus() == AspectPersistanceStatus.statusPersistent) {
firePerformAspect(stepControlAspect);
}
else {
LOGGER
.warn("Do not perform the aspect because the stepControlAspect is not stored: {}",
stepControlAspect);
}
}
@Override
public void activateOppositeAspect(final StepControlAspect stepControlAspect) {
LOGGER.info("Activate the aspect is called for new aspect: {}", stepControlAspect);
// make sure the aspect is valid and saved
if (stepControlAspect.isValid() && stepControlAspect.isOppositeValid()
&& stepControlAspect.getStatus() == AspectPersistanceStatus.statusPersistent) {
firePerformOppositeAspect(stepControlAspect);
}
else {
LOGGER
.warn("Do not perform the aspect because the stepControlAspect is not stored: {}",
stepControlAspect);
}
}
@Override
public void saveChanges(
final StepControlAspect originalAspect, Function func)
throws Exception {
StepControlAspect changedAspect = func.apply(originalAspect);
// the aspectDialog is only set if a new aspect is created
if (aspectDialog == null) {
LOGGER.info("Save the changed aspect: {}", changedAspect);
stepControlModel.updateStepControlAspect(originalAspect, changedAspect);
// expand the updated aspect
selectAspectByPosition(originalAspect);
}
else {
// new aspect is added
aspectDialog.setResult(JOptionPane.OK_OPTION);
LOGGER.info("Add the new aspect to the model: {}", changedAspect);
stepControlModel.addStepControlAspect(changedAspect);
// expand the inserted aspect
selectAspectByPosition(changedAspect);
aspectDialog.setVisible(false);
}
LOGGER.info("Set the pending changes flag.");
tabStatusListener.updatePendingChanges(component, true);
}
@Override
public void discardChanges(Callable func) throws Exception {
LOGGER.info("User decided to discard changes. Close the aspect dialog.");
func.call();
if (aspectDialog != null) {
aspectDialog.setResult(JOptionPane.CANCEL_OPTION);
aspectDialog.setVisible(false);
}
}
@Override
public void verifyUniquePosition(final StepControlAspect originalAspect, Long position, Long positionOpposite) {
// check if a stepControl aspect for the same position is configured already
verifyUniqueAspectPosition(originalAspect, position, positionOpposite);
}
private void verifyUniqueAspectPosition(
final StepControlAspect originalAspect, Long position, Long positionOpposite) {
if (position == null) {
throw new IllegalArgumentException("Provided position value is not available.");
}
if (TurnTableType.round == stepControlModel.getTurnTableType()) {
// check if a stepControl aspect for the same position is configured already
for (StepControlAspect currentAspect : stepControlModel.getStepControlAspects()) {
if ((Objects.equals(currentAspect.getPosition(), position) && currentAspect != originalAspect)
|| (Objects.equals(currentAspect.getOppositePosition(), positionOpposite)
&& currentAspect != originalAspect)) {
LOGGER
.warn("Found configured aspect with the same position or opposite position: {}",
currentAspect);
throw new IllegalArgumentException("Found configured aspect with the same position.");
}
}
for (StepControlAspect currentAspect : stepControlModel.getStepControlAspects()) {
if ((Objects.equals(currentAspect.getOppositePosition(), position)
&& currentAspect != originalAspect)
|| (Objects.equals(currentAspect.getPosition(), positionOpposite)
&& currentAspect != originalAspect)) {
LOGGER
.warn("Found configured aspect with the same position or opposite position: {}",
currentAspect);
throw new IllegalArgumentException("Found configured aspect with the same position.");
}
}
}
else {
// linear
for (StepControlAspect currentAspect : stepControlModel.getStepControlAspects()) {
if (Objects.equals(currentAspect.getPosition(), position) && currentAspect != originalAspect) {
LOGGER.warn("Found configured aspect with the same position: {}", currentAspect);
throw new IllegalArgumentException("Found configured aspect with the same position.");
}
}
}
}
@Override
public Long getCurrentTurntablePosition() {
return stepControlModel.getCurrentPosition();
}
}
private void fireCreateNewAspect() {
if (stepControlModel.getStepControlAspects().size() < stepControlModel.getMaxConfiguredAspects()) {
// check if the current selected row is dirty
int selectedRow = aspectTable.getSelectedRow();
if (selectedRow > -1) {
if (!aspectTable.checkAspectEditorIsDirty(selectedRow)) {
LOGGER.info("Check if aspect editor is dirty failed. Do not show the add aspect editor.");
return;
}
}
LOGGER.info("Create the editorPanel to create a new aspect.");
final StepControlAspect stepControlAspect = new StepControlAspect(null, null, Polarity.normal);
// create the callback listener for the new aspect
final AddAspectCallbackListener aspectCallbackListener = new AddAspectCallbackListener();
final AspectEditorPanel aspectEditorPanel =
aspectTable
.createAspectEditorPanel(aspectCallbackListener, stepControlAspect, stepControlModel,
EditorType.editorNew);
final AddAspectDialog aspectDialog =
new AddAspectDialog(JOptionPane.getFrameForComponent(component),
Resources.getString(StepControlPanel.class, "dialog_add_aspect"), true, aspectEditorPanel);
aspectCallbackListener.setAddAspectDialog(aspectDialog);
aspectDialog.setVisible(true);
if (aspectDialog.getResult() == JOptionPane.OK_OPTION) {
LOGGER.info("User added new aspect: {}", stepControlAspect);
}
else {
LOGGER.info("User cancelled add new aspect.");
}
aspectDialog.cleanup();
}
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);
}
}
/**
* Select the aspect by the position.
*
* @param stepControlAspect
* the aspect
*/
private void selectAspectByPosition(final StepControlAspect stepControlAspect) {
// select and expand the aspect
for (int rowIndex = 0; rowIndex < aspectTable.getRowCount(); rowIndex++) {
Object position = getSortableTableModel().getValueAt(rowIndex, AspectTableModel.COLUMN_POSITION);
LOGGER.info("Current position: {}", position);
Assert.isInstanceOf(StepControlAspect.class, position);
if (Objects.equals(position, stepControlAspect)) {
LOGGER
.info("Found configured aspect to select with the same position: {} at rowIndex: {}", position,
rowIndex);
aspectTable.setRowSelectionInterval(rowIndex, rowIndex);
LOGGER.info("Expand row: {}", rowIndex);
aspectTable.expandRow(rowIndex);
aspectTable.scrollRowToVisible(rowIndex);
break;
}
}
LOGGER.info("Select aspect by position has finished.");
}
private void fireRemoveAspect() {
LOGGER.info("Remove aspect.");
int selectedRow = aspectTable.getSelectedRow();
if (selectedRow > -1) {
int sortedRow = getSortableTableModel().getSortedRowAt(selectedRow);
AspectTableModel aspectTableModel = (AspectTableModel) getSortableTableModel().getActualModel();
Object aspect = aspectTableModel.getChildValueAt(sortedRow);
if (aspect instanceof StepControlAspect) {
StepControlAspect stepControlAspect = (StepControlAspect) aspect;
LOGGER.info("Remove aspect from stepControlModel: {}", stepControlAspect);
stepControlModel.removeStepControlAspect(stepControlAspect);
}
}
}
private void fireRemoveAllAspects() {
LOGGER.info("Remove all aspects.");
int totalRows = aspectTable.getRowCount();
if (totalRows > 0) {
// show a messagebox to check if the user really wants to delete all aspects
boolean continueDeleteAspects =
TaskDialogs
.build(JOptionPane.getFrameForComponent(component),
Resources.getString(StepControlPanel.class, "deleteAllAspects-confirm.instruction"),
Resources.getString(StepControlPanel.class, "deleteAllAspects-confirm"))
.title(Resources.getString(StepControlPanel.class, "deleteAllAspects-confirm.title")).isConfirmed();
LOGGER.info("The user confirmed to continue delete all aspects: {}", continueDeleteAspects);
if (continueDeleteAspects) {
removeAllAspects();
}
}
}
private void removeAllAspects() {
aspectTable.collapseAllRows();
stepControlModel.setStepControlAspects(null);
}
private void fireImportAspectsFromExcel() {
LOGGER.info("Import all aspects from excel.");
final WizardSettingsInterface wizardSettings = this.settingsService.getWizardSettings();
String storedWorkingDirectory = wizardSettings.getWorkingDirectory(WORKING_DIR_STEPCONTROL_EXPORT_KEY);
final FileNameExtensionFilter ff =
new FileNameExtensionFilter(
Resources.getString(StepControlPanel.class, "filter") + " (*."
+ org.bidib.wizard.utils.FileUtils.EXCEL_EXTENSION + ")",
org.bidib.wizard.utils.FileUtils.EXCEL_EXTENSION);
final FileDialog dialog = new FileDialog(component, FileDialog.OPEN, storedWorkingDirectory, null, ff) {
@Override
public void approve(String selectedFile) {
File file = new File(selectedFile);
selectedFile = file.getName();
try {
loadAspectsFromExcel(file);
final String exportDir = file.getParent();
LOGGER.info("Save current exportDir: {}", exportDir);
wizardSettings.setWorkingDirectory(WORKING_DIR_STEPCONTROL_EXPORT_KEY, exportDir);
}
catch (PositionOutOfRangeException ex) {
LOGGER.warn("Load aspects from excel file failed because invalid data was detected.", ex);
TaskDialogs
.build(JOptionPane.getFrameForComponent(getComponent()), Resources
.getString(StepControlPanel.class, "import-aspects-from-excel-invalid-data.instruction"),
Resources.getString(StepControlPanel.class, "import-aspects-from-excel-invalid-data.text")
+ Resources
.getString(StepControlPanel.class, "position-out-of-range.text",
ex.getAspectNumber(), ex.getPosition(), ex.getMin(), ex.getMax()))
.title(Resources.getString(StepControlPanel.class, "import-aspects-from-excel-failed.title"))
.showException(ex);
}
catch (IOException | DataExchangeException ex) {
LOGGER.warn("Load aspects from excel file failed.", ex);
TaskDialogs
.build(JOptionPane.getFrameForComponent(getComponent()),
Resources.getString(StepControlPanel.class, "import-aspects-from-excel-failed.instruction"),
Resources.getString(StepControlPanel.class, "import-aspects-from-excel-failed.text"))
.title(Resources.getString(StepControlPanel.class, "import-aspects-from-excel-failed.title"))
.showException(ex);
}
}
};
dialog.showDialog();
}
private static final String WORKING_DIR_STEPCONTROL_EXPORT_KEY = "stepControlExchange";
private void fireExportAspectsToExcel() {
LOGGER.info("Export all aspects to excel.");
final WizardSettingsInterface wizardSettings = this.settingsService.getWizardSettings();
String storedWorkingDirectory = wizardSettings.getWorkingDirectory(WORKING_DIR_STEPCONTROL_EXPORT_KEY);
String filename = "StepControl-export-" + NodeUtils.getNodeName(selectedNode) + ".xlsx";
if (!FileUtils.isFilenameValid(filename)) {
filename = FileUtils.escapeInvalidFilenameCharacters(filename, "_");
}
final FileNameExtensionFilter ff =
new FileNameExtensionFilter(
Resources.getString(StepControlPanel.class, "filter") + " (*."
+ org.bidib.wizard.utils.FileUtils.EXCEL_EXTENSION + ")",
org.bidib.wizard.utils.FileUtils.EXCEL_EXTENSION);
final FileDialog dialog = new FileDialog(component, FileDialog.SAVE, storedWorkingDirectory, filename, ff) {
@Override
public void approve(String selectedFile) {
File file = new File(selectedFile);
selectedFile = file.getName();
try {
writeAspectsToExcel(file);
final String exportDir = file.getParent();
LOGGER.info("Save current exportDir: {}", exportDir);
wizardSettings.setWorkingDirectory(WORKING_DIR_STEPCONTROL_EXPORT_KEY, exportDir);
}
catch (Exception ex) {
LOGGER.warn("Save aspects to excel file failed.", ex);
TaskDialogs
.build(JOptionPane.getFrameForComponent(getComponent()),
Resources.getString(StepControlPanel.class, "export-aspects-to-excel-failed.instruction"),
Resources.getString(StepControlPanel.class, "export-aspects-to-excel-failed.text"))
.title(Resources.getString(StepControlPanel.class, "export-aspects-to-excel-failed.title"))
.showException(ex);
}
}
};
dialog.showDialog();
}
private void loadAspectsFromExcel(File excelFile) throws IOException, PositionOutOfRangeException {
LOGGER.info("Load aspects from excel file: {}", excelFile);
List importAspects = null;
try (InputStream is = new BufferedInputStream(new FileInputStream(excelFile))) {
final ExcelAspectReader reader = new ExcelAspectReader();
importAspects = reader.readAspects(is);
}
if (CollectionUtils.isNotEmpty(importAspects)) {
LOGGER.info("Import the aspects.");
final List importAspectsOpposite = new ArrayList<>();
if (TurnTableType.round == stepControlModel.getTurnTableType()) {
int totalSteps = stepControlModel.getTotalSteps().intValue();
int oppositePositionStartPosition = totalSteps / 2;
// split the aspects
int size = importAspects.size();
LOGGER.info("Number of import aspects: {}", size);
importAspectsOpposite.addAll(importAspects.subList((size / 2), size));
importAspects.removeAll(importAspectsOpposite);
int aspectNumber = 0;
// check if we have invalid configuration, e.g. position value to big
// position values must be between 0 and half of motor steps
for (ImportAspect importAspect : importAspects) {
if (importAspect.getPosition() < 0 || importAspect.getPosition() >= oppositePositionStartPosition) {
LOGGER.warn("Invalid values detected: {}", importAspect);
throw new PositionOutOfRangeException(importAspect.getPosition(), 0,
oppositePositionStartPosition - 1, aspectNumber);
}
aspectNumber++;
}
for (ImportAspect importAspect : importAspectsOpposite) {
if (importAspect.getPosition() < oppositePositionStartPosition
|| importAspect.getPosition() >= totalSteps) {
LOGGER.warn("Invalid values detected: {}", importAspect);
throw new PositionOutOfRangeException(importAspect.getPosition(), oppositePositionStartPosition,
totalSteps - 1, aspectNumber);
}
aspectNumber++;
}
}
LOGGER.info("Current importAspects: {}", importAspects);
LOGGER.info("Current importAspectsOpposite: {}", importAspectsOpposite);
removeAllAspects();
final List stepControlAspects = new ArrayList<>();
int index = 0;
// add the imported aspects
for (ImportAspect importAspect : importAspects) {
StepControlAspect stepControlAspect =
new StepControlAspect(null, Long.valueOf(importAspect.getPosition()),
importAspect.isInverse() ? Polarity.inverted : Polarity.normal);
if (TurnTableType.round == stepControlModel.getTurnTableType()) {
ImportAspect importAspectOpposite = importAspectsOpposite.get(index);
stepControlAspect.setOppositePosition((long) importAspectOpposite.getPosition());
stepControlAspect
.setOppositePolarity(importAspectOpposite.isInverse() ? Polarity.inverted : Polarity.normal);
}
stepControlAspects.add(stepControlAspect);
index++;
}
stepControlModel.setStepControlAspects(stepControlAspects);
getSortableTableModel().fireTableDataChanged();
this.aspectTable.expandAllRows();
}
else {
LOGGER.info("No aspects loaded.");
}
}
private void writeAspectsToExcel(File excelFile) {
LOGGER.info("Write aspects to excel file: {}", excelFile);
final List importAspects = new ArrayList<>();
int aspectNumber = 0;
for (StepControlAspect aspect : stepControlModel.getStepControlAspects()) {
importAspects
.add(new ImportAspect(aspectNumber, aspect.getPosition().intValue(),
aspect.getPolarity() == Polarity.inverted));
aspectNumber++;
}
if (TurnTableType.round == stepControlModel.getTurnTableType()) {
// prepare the opposite positions
for (StepControlAspect aspect : stepControlModel.getStepControlAspects()) {
importAspects
.add(new ImportAspect(aspectNumber, aspect.getOppositePosition().intValue(),
aspect.getOppositePolarity() == Polarity.inverted));
aspectNumber++;
}
}
// provide the motor data
final Map cvNumberToNodeMap = getCvNumberToNodeMap(selectedNode);
Integer stepCount =
StepControlCvUtils
.getConfigVarIntValue(mapKeywordToNode, cvNumberToNodeMap, StepControlKeywords.KEYWORD_STEPCOUNT);
MicroStepsEnum microStepsEnum = StepControlCvUtils.getValidMicroStepping(mapKeywordToNode, cvNumberToNodeMap);
final MotorData motorData =
new MotorData(stepCount, microStepsEnum.getSteps(), stepControlModel.getTotalSteps().intValue(),
stepControlModel.getTurnTableType());
final ExcelAspectReader reader = new ExcelAspectReader();
reader.writeAspects(selectedNode, excelFile, importAspects, motorData);
}
private void fireConfigurationWizard() {
LOGGER.info("Open the configuration wizard.");
try {
Map cvNumberToNodeMap = getCvNumberToNodeMap(selectedNode);
final ConfigurationWizardModel configurationWizardModel =
ConfigurationWizard
.prepareConfigurationModel(selectedNode, stepControlModel.getTurnTableType(), cvNumberToNodeMap,
mapKeywordToNode);
final ConfigurationWizard wizard = new ConfigurationWizard(configurationWizardModel);
wizard.showWizard(component);
if (configurationWizardModel.getWizardStatus() == WizardStatus.finished) {
// transfer the new values to the node
writeConfigurationValues(configurationWizardModel, cvNumberToNodeMap, mapKeywordToNode);
}
else {
LOGGER.info("The configurationWizardModel is not available.");
}
}
catch (Exception ex) {
LOGGER.warn("Show configuration wizard failed.", ex);
TaskDialogs
.build(JOptionPane.getFrameForComponent(getComponent()),
Resources.getString(StepControlPanel.class, "show-config-wizard-failed.instruction"),
Resources.getString(StepControlPanel.class, "show-config-wizard-failed.text"))
.title(Resources.getString(StepControlPanel.class, "show-config-wizard-failed.title"))
.showException(ex);
}
}
/**
* The CV definition of the node has changed.
*/
public void cvDefinitionChanged() {
// the cvDefinitionChanged is misused to change the selected node
LOGGER.info("The cv definition has changed, selected node: {}", selectedNode);
if ((selectedNode != null && selectedNode.equals(mainModel.getSelectedNode()))
|| (selectedNode == null && (mainModel.getSelectedNode() == null
|| !ProductUtils.isStepControl(mainModel.getSelectedNode().getUniqueId())))) {
LOGGER.info("The node in the model has not changed or is not a StepControl.");
return;
}
if (selectedNode != null) {
LOGGER.info("Remove the port value listener from the previous selected node.");
selectedNode.removePortValueListener(MotorPort.class, this);
}
LOGGER.info("Reset the cv values loaded flag.");
cvValuesLoaded.set(false);
// force reload of CV values
initialized = false;
if (readCvButton != null) {
readCvButton.setEnabled(false);
}
if (writeCvButton != null) {
writeCvButton.setEnabled(false);
}
this.selectedNode = null;
// set the selected node
final NodeInterface currentSelectedNode = mainModel.getSelectedNode();
if (ProductUtils.isStepControl(currentSelectedNode.getUniqueId())) {
this.selectedNode = currentSelectedNode;
}
// clear the aspects
// set the aspects silently
try {
aspectTable.getModel().removeTableModelListener(aspectTableModelListener);
stepControlModel.clearModel();
}
finally {
aspectTable.getModel().addTableModelListener(aspectTableModelListener);
}
requiredConfigVariables.clear();
if (mapKeywordToNode != null) {
LOGGER.info("Clear the mapKeywordToNode.");
mapKeywordToNode.clear();
}
// reset the pending changes flag on the tab
resetPendingChanges();
updateToolbarButtons();
// load the aspects initially
if (selectedNode != null && ProductUtils.isStepControl(selectedNode.getUniqueId())) {
LOGGER.info("The currently selected node is a StepControl.");
selectedNode.addPortValueListener(MotorPort.class, this);
fieldsToUpdate.add(StepControlKeywords.KEYWORD_CONFIGURED_ASPECTS);
}
}
/**
* The values of the current CV definition have changed. Get the cv values and update the stepControlModel.
*/
public void cvDefinitionValuesChanged(final boolean read, final List changedNames) {
LOGGER.info("The cv definition values have changed, read: {}, changedNames: {}", read, changedNames);
cvDefinitionChanged();
if (selectedNode == null || !ProductUtils.isStepControl(selectedNode.getUniqueId())) {
LOGGER.info("The currently selected node is not a StepControl.");
return;
}
final Map cvNumberToNodeMap = getCvNumberToNodeMap(selectedNode);
final AtomicBoolean errorDetected = new AtomicBoolean();
if (changedNames.contains(StepControlKeywords.CVKEY_CURRENT_POSITION)) {
try {
long currentPosition =
StepControlCvUtils
.getConfigVarLongValueFromStringByCvKey(cvNumberToNodeMap,
StepControlKeywords.CVKEY_CURRENT_POSITION);
stepControlModel.setCurrentPosition(currentPosition);
LOGGER
.info(
"The current position was read and no more CV values are updated! The current position is: {}",
currentPosition);
}
catch (Exception ex) {
LOGGER.warn("Update the current position failed.", ex);
}
// return;
}
if (MapUtils.isEmpty(mapKeywordToNode)) {
LOGGER.info("No mapKeywordToNode value available.");
return;
}
if (changedNames.contains(getCvName(StepControlKeywords.KEYWORD_TOTALSTEPS))) {
try {
Long totalSteps =
StepControlCvUtils.getConfigVarLongValue(mapKeywordToNode, StepControlKeywords.KEYWORD_TOTALSTEPS);
LOGGER.info("Current total steps: {}", totalSteps);
stepControlModel.setTotalSteps(totalSteps);
if (angleRenderer != null) {
angleRenderer.setTotalSteps(totalSteps);
}
}
catch (Exception ex) {
LOGGER.warn("Set the total step count failed.", ex);
errorDetected.set(true);
}
}
if (changedNames.contains(getCvName(StepControlKeywords.KEYWORD_TABLETYPE))) {
try {
Integer value =
StepControlCvUtils
.getConfigVarIntValue(mapKeywordToNode, cvNumberToNodeMap,
StepControlKeywords.KEYWORD_TABLETYPE);
TurnTableType turnTableType = TurnTableType.fromValue(ByteUtils.getLowByte(value));
stepControlModel.setTurnTableType(turnTableType);
int maxConfiguredAspects = StepControlControllerInterface.MAX_CONFIGURED_ASPECTS;
switch (turnTableType) {
case round:
maxConfiguredAspects = StepControlControllerInterface.MAX_CONFIGURED_ASPECTS / 2;
break;
case pendular:
maxConfiguredAspects = 2;
break;
default:
break;
}
stepControlModel.setMaxConfiguredAspects(maxConfiguredAspects);
LOGGER
.info("The turnTableType was set to: {}, maxConfiguredAspects: {}", turnTableType,
maxConfiguredAspects);
}
catch (Exception ex) {
LOGGER.warn("Set the turnTableType failed.", ex);
errorDetected.set(true);
}
}
if (changedNames.contains(getCvName(StepControlKeywords.KEYWORD_SPEED))) {
try {
Integer speed =
StepControlCvUtils
.getConfigVarIntValue(mapKeywordToNode, cvNumberToNodeMap, StepControlKeywords.KEYWORD_SPEED);
stepControlModel.setSpeed(speed);
}
catch (Exception ex) {
LOGGER.warn("Set the speed failed.", ex);
errorDetected.set(true);
}
}
if (changedNames.contains(getCvName(StepControlKeywords.KEYWORD_ACCEL))) {
try {
Integer accel =
StepControlCvUtils
.getConfigVarIntValue(mapKeywordToNode, cvNumberToNodeMap, StepControlKeywords.KEYWORD_ACCEL);
stepControlModel.setAccel(accel);
}
catch (Exception ex) {
LOGGER.warn("Set the accel failed.", ex);
errorDetected.set(true);
}
}
if (changedNames.contains(getCvName(StepControlKeywords.KEYWORD_DECEL))) {
try {
Integer decel =
StepControlCvUtils
.getConfigVarIntValue(mapKeywordToNode, cvNumberToNodeMap, StepControlKeywords.KEYWORD_DECEL);
stepControlModel.setDecel(decel);
}
catch (Exception ex) {
LOGGER.warn("Set the decel failed.", ex);
errorDetected.set(true);
}
}
if (changedNames.contains(getCvName(StepControlKeywords.KEYWORD_STEPCOUNT))
|| changedNames.contains(getCvName(StepControlKeywords.KEYWORD_UNIT_SYSTEM))) {
final Integer stepCount =
StepControlCvUtils
.getConfigVarIntValue(mapKeywordToNode, cvNumberToNodeMap, StepControlKeywords.KEYWORD_STEPCOUNT);
final MicroStepsEnum microStepsEnum =
StepControlCvUtils.getValidMicroStepping(mapKeywordToNode, cvNumberToNodeMap);
// unit system
Integer unitSystemValue =
StepControlCvUtils
.getConfigVarByteValue(mapKeywordToNode, cvNumberToNodeMap,
StepControlKeywords.KEYWORD_UNIT_SYSTEM);
MovementScaleEnum movementScale = MovementScaleEnum.scale1;
if (unitSystemValue != null) {
int bitSpeed = ByteUtils.getBit(unitSystemValue, 0);
movementScale = (bitSpeed > 0 ? MovementScaleEnum.scale0_1 : MovementScaleEnum.scale1);
}
// check max valid speed
long maxSpeedValid =
SpeedRangeValidationUtils.calculateValidMaxSpeed(microStepsEnum.getSteps(), stepCount, movementScale);
int currentMaxSpeed = boundedRangeAdapterSpeed.getMaximum();
LOGGER.info("Current speed range maximum in bounded range adapter is: {}", currentMaxSpeed);
if (currentMaxSpeed > maxSpeedValid) {
LOGGER
.info(
"The configured maxSpeed ({}) in bounded range adapter exceeds the limit: {}. Update the maxSpeed in bounded range adapter to limit.",
currentMaxSpeed, maxSpeedValid);
boundedRangeAdapterSpeed.setMaximum((int) maxSpeedValid);
int currentSpeed = stepControlModel.getSpeed();
if (currentSpeed > maxSpeedValid) {
LOGGER.warn("Set the current speed to the maximum valid speed: {}", maxSpeedValid);
stepControlModel.setSpeed((int) maxSpeedValid);
}
if (speedSlider != null) {
speedSlider.updateUI();
}
}
// check max valid accel and decel values
try {
long accelDecelLimit = SpeedRangeValidationUtils.F_STEP_ACCEL_LIMITx10;
AccelarationScaleEnum accelarationScale = AccelarationScaleEnum.scale1;
if (unitSystemValue != null) {
int bitAccelaration = ByteUtils.getBit(unitSystemValue, 4);
accelarationScale =
(bitAccelaration > 0 ? AccelarationScaleEnum.scale0_1 : AccelarationScaleEnum.scale1);
}
if (accelarationScale == AccelarationScaleEnum.scale0_1) {
accelDecelLimit = SpeedRangeValidationUtils.F_STEP_ACCEL_LIMIT;
}
long accelarationFactor =
SpeedRangeValidationUtils.calculateValidAccelarationFactor(microStepsEnum.getSteps(), stepCount);
// check and adjust accel
long minAccel = accelarationFactor * stepControlModel.getAccel();
LOGGER.info("The calculated minAccel: {}, current accelDecelLimit: {}", minAccel, accelDecelLimit);
if (minAccel < accelDecelLimit) {
long adjustedAccelValue = accelDecelLimit / accelarationFactor;
LOGGER.warn("Set the current accel value to minimum: {}", adjustedAccelValue);
stepControlModel.setAccel((int) adjustedAccelValue);
}
// check and adjust decel
long minDecel = accelarationFactor * stepControlModel.getDecel();
LOGGER.info("The calculated minDecel: {}, current accelDecelLimit: {}", minDecel, accelDecelLimit);
if (minDecel < accelDecelLimit) {
long adjustedDecelValue = accelDecelLimit / accelarationFactor;
LOGGER.warn("Set the current decel value to minimum: {}", adjustedDecelValue);
stepControlModel.setDecel((int) adjustedDecelValue);
}
}
catch (Exception ex) {
LOGGER.warn("Set or adjust the decel value failed.", ex);
errorDetected.set(true);
}
}
if (changedNames.contains(getCvName(StepControlKeywords.KEYWORD_PUSH_INTERVAL))) {
try {
Integer pushInterval =
StepControlCvUtils
.getConfigVarByteValue(mapKeywordToNode, cvNumberToNodeMap,
StepControlKeywords.KEYWORD_PUSH_INTERVAL);
stepControlModel.setPushInterval(pushInterval);
}
catch (Exception ex) {
LOGGER.warn("Set the push interval failed.", ex);
errorDetected.set(true);
}
}
if (changedNames.contains(getCvName(StepControlKeywords.KEYWORD_BRIDGE))) {
Integer cvNodeBridge =
StepControlCvUtils
.getConfigVarByteValue(mapKeywordToNode, cvNumberToNodeMap, StepControlKeywords.KEYWORD_BRIDGE);
LOGGER.info("Current cvNodeBridge: {}", cvNodeBridge);
if (cvNodeBridge != null) {
int bridge = 0;
try {
bridge = cvNodeBridge.intValue();
LOGGER.info("Current bridge value: {}", bridge);
}
catch (Exception ex) {
LOGGER.warn("Get the bridge value from CVs failed.", ex);
}
// if (bridge == 1 /* none */) {
// // hide the servos and light ports
// LOGGER.info("Remove the servos and light ports because no bridge is configured.");
// selectedNode.setServoPorts(Collections.emptyList());
// mainModel.setLightPorts(Collections.emptyList());
// }
}
}
// do not always update the configured aspects
if (!initialized && read) {
LOGGER.info("The values were read, update the aspects in the table.");
fieldsToUpdate.add(StepControlKeywords.KEYWORD_CONFIGURED_ASPECTS);
}
// prepare the aspects
if (fieldsToUpdate.contains(StepControlKeywords.KEYWORD_CONFIGURED_ASPECTS)) {
LOGGER.info("Prepare the configured aspects.");
// create the list of aspects with the values from the config variables
final List configuredAspects =
prepareConfiguredAspects(cvNumberToNodeMap, errorDetected);
// set the aspects silently
try {
aspectTable.getModel().removeTableModelListener(aspectTableModelListener);
stepControlModel.setStepControlAspects(configuredAspects);
}
finally {
aspectTable.getModel().addTableModelListener(aspectTableModelListener);
}
fieldsToUpdate.remove(StepControlKeywords.KEYWORD_CONFIGURED_ASPECTS);
getSortableTableModel().fireTableDataChanged();
this.aspectTable.expandAllRows();
}
if (!initialized && !errorDetected.get()) {
initialized = true;
LOGGER.info("The panel was initialized. Reset the pending changes flag on the tab.");
tabStatusListener.updatePendingChanges(component, false);
}
else if (read) {
LOGGER.info("The CV values were read from the node. Reset the pending changes flag on the tab.");
tabStatusListener.updatePendingChanges(component, false);
}
else {
// check if we have pending changes on the aspects
if (!checkHasPendingAspects(cvNumberToNodeMap)) {
LOGGER.info("No pending aspect changes detected. Reset the pending changes flag on the tab.");
tabStatusListener.updatePendingChanges(component, false);
}
}
// update the buttons in the toolbar
updateToolbarButtons();
}
private void updateToolbarButtons() {
if (selectedNode != null) {
if (readCvButton != null) {
// enable the read button to get the values of the configuration variables for this node on
// request
readCvButton.setEnabled(MapUtils.isNotEmpty(selectedNode.getConfigVariables()));
}
if (writeCvButton != null) {
// enable the write button to write the values of the configuration variables for this node on
// request
writeCvButton.setEnabled(MapUtils.isNotEmpty(selectedNode.getConfigVariables()));
}
}
}
private String getCvName(String keyword) {
final CvNode cvNode = StepControlCvUtils.getNode(mapKeywordToNode, keyword);
if (cvNode != null) {
return cvNode.getConfigVar().getName();
}
return null;
}
/**
* Prepare the configured aspects of the turntable from the CV values.
*
* @param cvNumberToJideNodeMap
* the cv number to node map
* @param errorDetected
* holder for error detected flag
* @return the list of aspects
*/
private List prepareConfiguredAspects(
final Map cvNumberToJideNodeMap, final AtomicBoolean errorDetected) {
// If round turntable is configured then the aspects are symetric. This means that only half of the aspects
// are usable for tracks because the other half is used for the opposite position.
boolean roundTurnTable = TurnTableType.round == stepControlModel.getTurnTableType();
long oppositePositionStartPosition = -1;
Long totalSteps = stepControlModel.getTotalSteps();
if (totalSteps != null && totalSteps.longValue() > 0) {
oppositePositionStartPosition = totalSteps.longValue() / 2;
}
LOGGER
.info("Prepare the aspects for turntable type: {}, totalSteps: {}, oppositePositionStartPosition: {}",
stepControlModel.getTurnTableType(), totalSteps, oppositePositionStartPosition);
// keep the first aspect with opposite position
StepControlAspect firstOppositeStepControlAspect = null;
int maxAspectCount = 0;
// create the list of aspects with the values from the config variables
final List configuredAspects = new ArrayList<>();
int maxIndex = StepControlControllerInterface.MAX_CONFIGURED_ASPECTS;
for (int index = 0; index < maxIndex; index++) {
try {
Integer targetPolarity =
StepControlCvUtils
.getConfigVarIntValue(mapKeywordToNode, cvNumberToJideNodeMap,
String.format(StepControlKeywords.KEYWORD_PATTERN_POLARITY, index));
Long targetPosition =
StepControlCvUtils
.getConfigVarLongValue(mapKeywordToNode,
String.format(StepControlKeywords.KEYWORD_PATTERN_POSITION, index));
if (targetPosition < INACTIVE_POSITION_VALUE) {
StepControlAspect stepControlAspect =
new StepControlAspect(null, targetPosition.longValue(),
Polarity.valueOf(targetPolarity.intValue()));
stepControlAspect.setStatus(AspectPersistanceStatus.statusPersistent);
LOGGER.info("Current index: {}, adding new stepControlAspect: {}", index, stepControlAspect);
configuredAspects.add(stepControlAspect);
if (roundTurnTable && firstOppositeStepControlAspect == null
&& stepControlAspect.getPosition().longValue() > oppositePositionStartPosition) {
firstOppositeStepControlAspect = stepControlAspect;
LOGGER
.info(
"Found the first opposite position aspect based on the position and totalSteps at position: {}, firstOppositeStepControlAspect: {}",
stepControlAspect.getPosition().longValue(), firstOppositeStepControlAspect);
maxAspectCount = configuredAspects.size() - 1;
}
}
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());
errorDetected.set(true);
}
catch (Exception ex) {
LOGGER.warn("Prepare configured step control aspect failed.", ex);
errorDetected.set(true);
}
}
// process the configured aspects
if (TurnTableType.round == stepControlModel.getTurnTableType()) {
// The problem with round type is that we must first detect the total configured number of aspects
// and then assume that the second half is the opposite. Maybe we can check if the position value is
// more than the half number of steps.
int maxTrackPositions = configuredAspects.size();
LOGGER.info("Number of aspects read from cv values: {}", maxTrackPositions);
if (maxAspectCount == 0 && maxTrackPositions > 0) {
maxAspectCount = maxTrackPositions / 2;
}
final List mergedConfiguredAspects = new ArrayList<>();
// when we get the opposite aspect we must check if the position is more
// than the half of total steps.
boolean foundFirstOppositePosition = false;
for (int index = 0; index < maxAspectCount; index++) {
StepControlAspect stepControlAspect = configuredAspects.get(index);
StepControlAspect stepControlAspectOpposite = null;
if ((index + maxAspectCount) < maxTrackPositions) {
stepControlAspectOpposite = configuredAspects.get(index + maxAspectCount);
}
LOGGER
.info("Fetched stepControlAspect: {}, stepControlAspectOpposite: {}", stepControlAspect,
stepControlAspectOpposite);
if (stepControlAspectOpposite != null && stepControlAspectOpposite.getPosition() != null
&& stepControlAspectOpposite.getPosition().longValue() >= oppositePositionStartPosition) {
if (!foundFirstOppositePosition) {
LOGGER.info("The first opposite position was found: {}", stepControlAspectOpposite);
foundFirstOppositePosition = true;
}
LOGGER
.info("Merge the values from opposite aspect: {}, stepControlAspect: {}",
stepControlAspectOpposite, stepControlAspect);
stepControlAspect.setOppositePosition(stepControlAspectOpposite.getPosition());
stepControlAspect.setOppositePolarity(stepControlAspectOpposite.getPolarity());
// keep a reference to the opposite aspect
LOGGER.info("Set the opposite aspect on aspect: {}", stepControlAspect);
stepControlAspect.setOppositeAspect(stepControlAspectOpposite);
}
LOGGER.info("Adding merged stepControlAspect: {}", stepControlAspect);
mergedConfiguredAspects.add(stepControlAspect);
}
configuredAspects.clear();
configuredAspects.addAll(mergedConfiguredAspects);
}
else if (TurnTableType.linear == stepControlModel.getTurnTableType()) {
// linear turntable
}
else if (TurnTableType.pendular == stepControlModel.getTurnTableType()) {
// pendular turntable
// remove the surplus aspects, only 2 aspects allowed
if (configuredAspects.size() > 2) {
// get only the first 2 aspects from the model
// configuredAspects.clear();
final List subListAspects = new ArrayList<>(configuredAspects.subList(0, 2));
configuredAspects.clear();
configuredAspects.addAll(subListAspects);
}
}
return configuredAspects;
}
public void executionStateChanged(
AccessoryExecutionState executionState, Integer accessoryId, Integer aspect, AccessoryState accessoryState) {
LOGGER
.info("The execution state has changed: {}, accessoryId: {}, aspect: {}", executionState, accessoryId,
aspect);
if (aspect == null || executionState == null) {
executionState = AccessoryExecutionState.IDLE;
}
executionStateIconLabel.setToolTipText(null);
switch (executionState) {
case ERROR:
executionStateIconLabel.setIcon(accessoryErrorIcon);
executionStateIconLabel
.setText(Resources.getString(StepControlPanel.class, "unknown_error_aspect", aspect));
if (accessoryState != null) {
executionStateIconLabel.setToolTipText(accessoryState.getErrorInformation());
}
break;
case RUNNING:
executionStateIconLabel.setIcon(accessoryWaitIcon);
executionStateIconLabel
.setText(Resources.getString(StepControlPanel.class, "executing_aspect", aspect));
break;
case SUCCESSFUL:
executionStateIconLabel.setIcon(accessorySuccessfulIcon);
executionStateIconLabel.setText(Resources.getString(StepControlPanel.class, "active_aspect", aspect));
break;
case UNKNOWN:
executionStateIconLabel.setIcon(accessoryUnknownIcon);
executionStateIconLabel.setText(null);
break;
default:
executionStateIconLabel.setIcon(null);
executionStateIconLabel.setText(null);
break;
}
synchronized (aspectExecutionModel) {
if (aspectExecutionModel.getAccessoryId() == accessoryId && aspectExecutionModel.getAspectId() == aspect) {
LOGGER.info("Update the aspectExecutionModel.");
aspectExecutionModel.setExecutionState(executionState);
aspectExecutionModel.setAccessoryState(accessoryState);
}
}
}
private void addToolBarButtons(VLToolBar toolBar) {
// read CV button
readCvButton =
makeNavigationButton("loadfromnode", "/32x32", READ, Resources.getString(getClass(), "toolbar.readallcv"),
Resources.getString(getClass(), "toolbar.readallcv.alttext"));
readCvButton.setEnabled(false);
toolBar.add(readCvButton);
readCvButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
readCurrentValuesFromCV(selectedNode);
}
});
// write CV button
writeCvButton =
makeNavigationButton("savetonode", "/32x32", WRITE, Resources.getString(getClass(), "toolbar.writeallcv"),
Resources.getString(getClass(), "toolbar.writeallcv.alttext"));
toolBar.add(writeCvButton);
writeCvButton.setEnabled(false);
writeCvButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
prepareAndWriteCvValuesToNode(false);
}
catch (InvalidAspectException ex) {
LOGGER.warn("Write CV value to node failed because an invalid aspect was detected.", ex);
int currentAspectIndex = ex.getAspectIndex();
if (currentAspectIndex > -1) {
LOGGER.info("Invalid data in currentAspectIndex: {}", currentAspectIndex);
aspectTable.expandRow(currentAspectIndex);
aspectTable.scrollRowToVisible(currentAspectIndex);
}
TaskDialogs
.build(JOptionPane.getFrameForComponent(getComponent()),
Resources.getString(StepControlPanel.class, "write-aspect-failed.instruction"),
Resources.getString(StepControlPanel.class, "write-aspect-failed.text", currentAspectIndex))
.title(Resources.getString(StepControlPanel.class, "write-aspect-failed.title"))
.showException(ex);
}
catch (Exception ex) {
LOGGER.warn("Write CV value to node failed.", ex);
TaskDialogs
.build(JOptionPane.getFrameForComponent(getComponent()),
Resources.getString(StepControlPanel.class, "write-cv-failed.instruction"),
Resources.getString(StepControlPanel.class, "write-cv-failed.text"))
.title(Resources.getString(StepControlPanel.class, "write-cv-failed.title")).showException(ex);
}
}
});
}
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(DefaultApplicationContext.KEY_TOPTOOLBARPANEL);
topToolBarPanel.add(toolBar, constraints);
}
private void prepareAndWriteCvValuesToNode(boolean writeOnlyAspects) {
LOGGER.info("Write the CV values, writeOnlyAspects: {}", writeOnlyAspects);
final Map cvNumberToNodeMap = getCvNumberToNodeMap(selectedNode);
// prepare the cv list to write
final List cvList = new ArrayList<>();
// collect the new values
int aspectIndex = 0;
// get the aspects from the controller
final List aspects = this.stepControlController.getConfigureAspectsListModel();
// check the aspects
for (StepControlAspect currentAspect : aspects) {
LOGGER.info("Prepare aspect to save: {}", currentAspect);
// prepare the CV values of all aspects
prepareCvValues(aspectIndex, currentAspect, cvList, cvNumberToNodeMap);
aspectIndex++;
}
// add the terminating aspects by setting 0xFFFF as position
if (aspectIndex < StepControlControllerInterface.MAX_CONFIGURED_ASPECTS) {
StepControlAspect currentAspect = new StepControlAspect(null, INACTIVE_POSITION_VALUE, Polarity.normal);
LOGGER.info("Add the terminating aspect: {}", currentAspect);
prepareCvValues(aspectIndex, currentAspect, cvList, cvNumberToNodeMap);
aspectIndex++;
}
if (!writeOnlyAspects) {
// check the turntable type
final CvNode tableTypeNode =
StepControlCvUtils.getNode(mapKeywordToNode, StepControlKeywords.KEYWORD_TABLETYPE);
TurnTableType turnTableType = stepControlModel.getTurnTableType();
CvValueUtils
.compareAndAddNewValue(tableTypeNode, Integer.toString(turnTableType.getCvValue()), cvList,
cvNumberToNodeMap);
// check the speed
CvNode speedNode = StepControlCvUtils.getNode(mapKeywordToNode, StepControlKeywords.KEYWORD_SPEED);
int speed = stepControlModel.getSpeed();
CvValueUtils.compareAndAddNewValue(speedNode, Integer.toString(speed), cvList, cvNumberToNodeMap);
// check the accel
CvNode accelNode = StepControlCvUtils.getNode(mapKeywordToNode, StepControlKeywords.KEYWORD_ACCEL);
int accel = stepControlModel.getAccel();
CvValueUtils.compareAndAddNewValue(accelNode, Integer.toString(accel), cvList, cvNumberToNodeMap);
// check the decel
CvNode decelNode = StepControlCvUtils.getNode(mapKeywordToNode, StepControlKeywords.KEYWORD_DECEL);
int decel = stepControlModel.getDecel();
CvValueUtils.compareAndAddNewValue(decelNode, Integer.toString(decel), cvList, cvNumberToNodeMap);
}
else {
LOGGER.info("Only write aspects to node.");
}
final CvDefinitionPanelProvider cvDefinitionPanelProvider = StepControlPanel.this;
// write the values on the node
CvValueUtils.writeCvValues(selectedNode, cvList, cvNumberToNodeMap, cvDefinitionPanelProvider);
if (!hasPendingChanges()) {
// set the persistence status of all aspects to persistent
LOGGER.info("Set the persistence status of all aspects to persistent.");
for (StepControlAspect currentAspect : stepControlModel.getStepControlAspects()) {
currentAspect.setStatus(AspectPersistanceStatus.statusPersistent);
}
}
else {
LOGGER.info("Pending changes detected after write CV values to node.");
}
}
private boolean checkHasPendingAspects(final Map cvNumberToNodeMap) {
// collect the new values
int aspectIndex = 0;
// get the aspects from the controller
final List aspects = this.stepControlController.getConfigureAspectsListModel();
// check the aspects
for (StepControlAspect currentAspect : aspects) {
LOGGER.info("Prepare aspect to save: {}", currentAspect);
// prepare the CV values of all aspects
boolean changeDetected = checkChangedCvValues(aspectIndex, currentAspect, cvNumberToNodeMap);
if (changeDetected) {
return true;
}
aspectIndex++;
}
// add the terminating aspects by setting 0xFFFF as position
if (aspectIndex < StepControlControllerInterface.MAX_CONFIGURED_ASPECTS) {
StepControlAspect currentAspect = new StepControlAspect(null, INACTIVE_POSITION_VALUE, Polarity.normal);
LOGGER.info("Add the terminating aspect: {}", currentAspect);
boolean changeDetected = checkChangedCvValues(aspectIndex, currentAspect, cvNumberToNodeMap);
if (changeDetected) {
return true;
}
aspectIndex++;
}
return false;
}
private Map getCvNumberToNodeMap(final NodeInterface node) {
// get the prepared CvDefinitionTreeTableModel from the CvDefinitionPanelController
CvDefinitionPanelController cvDefinitionPanelController =
DefaultApplicationContext
.getInstance()
.get(DefaultApplicationContext.KEY_CVDEFINITIONPANEL_CONTROLLER, CvDefinitionPanelController.class);
final Map cvNumberToNodeMap = cvDefinitionPanelController.getCvNumberToNodeMap(node);
return cvNumberToNodeMap;
}
/**
* Write the CV values provided by the configurationWizardModel.
*
* @param configurationWizardModel
* the configuration wizard model
*/
private void writeConfigurationValues(
final ConfigurationWizardModel configurationWizardModel, final Map cvNumberToNodeMap,
final Map mapKeywordToNode) {
LOGGER.info("Write the values from the configuration wizard model to the node.");
// prepare the cv list to write
final List cvList = new ArrayList<>();
// check the turntable type
CvNode tableTypeNode = StepControlCvUtils.getNode(mapKeywordToNode, StepControlKeywords.KEYWORD_TABLETYPE);
TurnTableType turnTableType = configurationWizardModel.getTurnTableType();
stepControlModel.setTurnTableType(turnTableType);
CvValueUtils
.compareAndAddNewValue(tableTypeNode, Integer.toString(turnTableType.getCvValue()), cvList,
cvNumberToNodeMap);
// check the motor type
CvNode nemaTypeNode = StepControlCvUtils.getNode(mapKeywordToNode, StepControlKeywords.KEYWORD_NEMATYPE);
MotorSizeType motorSizeType = configurationWizardModel.getMotorSizeType();
CvValueUtils
.compareAndAddNewValue(nemaTypeNode, Integer.toString(motorSizeType.getCvValue()), cvList,
cvNumberToNodeMap);
// // prepare the current moving and stopped values
// CvNode currentMovingNode = getNode(StepControlKeywords.KEYWORD_CURRENT_MOVING);
// // the values are stored in 10mA units
// int currentMoving = motorSizeType.getCurrentMoving();
// CvValueUtils
// .compareAndAddNewValue(currentMovingNode, Integer.toString(currentMoving / 10), cvList, cvNumberToNodeMap);
// // prepare the current moving and stopped values
// CvNode currentStoppedNode = getNode(StepControlKeywords.KEYWORD_CURRENT_STOPPED);
// // the values are stored in 10mA units
// int currentStopped = motorSizeType.getCurrentStopped();
// CvValueUtils
// .compareAndAddNewValue(currentStoppedNode, Integer.toString(currentStopped / 10), cvList,
// cvNumberToNodeMap);
// check the step count
CvNode stepCountNode = StepControlCvUtils.getNode(mapKeywordToNode, StepControlKeywords.KEYWORD_STEPCOUNT);
Integer stepCount = configurationWizardModel.getStepCount();
// make sure step count is available and not zero
if (stepCount == null || stepCount == 0) {
LOGGER.warn("Adjusting invalid stepCount to default value 1.");
stepCount = 1;
}
CvValueUtils.compareAndAddNewValue(stepCountNode, Integer.toString(stepCount), cvList, cvNumberToNodeMap);
// check the gear primary
CvNode gearPrimaryNode = StepControlCvUtils.getNode(mapKeywordToNode, StepControlKeywords.KEYWORD_GEAR_PRIMARY);
Integer gearRatioPrimary = configurationWizardModel.getGearing().getGearRatioPrimary();
CvValueUtils
.compareAndAddNewValue(gearPrimaryNode, Integer.toString(gearRatioPrimary), cvList, cvNumberToNodeMap);
// check the gear secondary
CvNode gearSecondaryNode =
StepControlCvUtils.getNode(mapKeywordToNode, StepControlKeywords.KEYWORD_GEAR_SECONDARY);
Integer gearRatioSecondary = configurationWizardModel.getGearing().getGearRatioSecondary();
CvValueUtils
.compareAndAddNewValue(gearSecondaryNode, Integer.toString(gearRatioSecondary), cvList, cvNumberToNodeMap);
// check the total steps
LongCvNode totalStepsNode =
(LongCvNode) StepControlCvUtils.getNode(mapKeywordToNode, StepControlKeywords.KEYWORD_TOTALSTEPS);
Integer totalStepCount = configurationWizardModel.getTotalStepCount();
CvValueUtils.compareAndAddNewValue(totalStepsNode, totalStepCount, cvList);
// prepare the gearing
if (Gearing.YES.equals(configurationWizardModel.getGearing().getKey())) {
CvNode backlashNode = StepControlCvUtils.getNode(mapKeywordToNode, StepControlKeywords.KEYWORD_BACKLASH);
Integer backlash = configurationWizardModel.getGearing().getBackLash();
CvValueUtils.compareAndAddNewValue(backlashNode, Integer.toString(backlash), cvList, cvNumberToNodeMap);
}
else {
LOGGER.info("No gearing selected.");
CvNode backlashNode = StepControlCvUtils.getNode(mapKeywordToNode, StepControlKeywords.KEYWORD_BACKLASH);
Integer backlash = 0;
CvValueUtils.compareAndAddNewValue(backlashNode, Integer.toString(backlash), cvList, cvNumberToNodeMap);
}
// check the microstepping
CvNode microSteppingNode =
StepControlCvUtils.getNode(mapKeywordToNode, StepControlKeywords.KEYWORD_MICROSTEPPING);
Integer microStepping =
(configurationWizardModel.getMicroStepping() != null
? configurationWizardModel.getMicroStepping().getSteps() : null);
// make sure microStepping is available and not zero
if (microStepping == null || microStepping == 0) {
LOGGER.warn("Adjusting invalid microstepping to default value 64.");
microStepping = 64;
}
CvValueUtils
.compareAndAddNewValue(microSteppingNode, Integer.toString(microStepping), cvList, cvNumberToNodeMap);
// write the values on the node
CvValueUtils.writeCvValues(selectedNode, cvList, cvNumberToNodeMap, StepControlPanel.this);
// update the renderer
try {
Long totalSteps =
StepControlCvUtils.getConfigVarLongValue(mapKeywordToNode, StepControlKeywords.KEYWORD_TOTALSTEPS);
if (angleRenderer != null) {
angleRenderer.setTotalSteps(totalSteps);
}
}
catch (Exception ex) {
LOGGER.warn("Set the total step count failed.", ex);
}
}
private void prepareCvValues(
int aspectIndex, StepControlAspect currentAspect, final List cvList,
final Map cvNumberToNodeMap) {
// prepare the keyword to search
String keywordPolarity = String.format(StepControlKeywords.KEYWORD_PATTERN_POLARITY, aspectIndex);
CvNode polarityNode = StepControlCvUtils.getNode(mapKeywordToNode, keywordPolarity);
Polarity polarity = currentAspect.getPolarity();
CvValueUtils
.compareAndAddNewValue(polarityNode, Integer.toString(polarity.getCvValue()), cvList, cvNumberToNodeMap);
// the target position is a long type
String keywordPosition = String.format(StepControlKeywords.KEYWORD_PATTERN_POSITION, aspectIndex);
// get the master
LongCvNode positionNode = (LongCvNode) StepControlCvUtils.getNode(mapKeywordToNode, keywordPosition);
Long position = currentAspect.getPosition();
// prepare the master value
LOGGER.debug("The new position is: {}", position);
if (position != null) {
CvValueUtils.compareAndAddNewValue(positionNode, Long.toString(position), cvList, cvNumberToNodeMap);
}
else {
LOGGER.warn("No position of aspect available. Current aspect: {}", currentAspect);
}
}
/**
* Check for changed cv values but do not touch the cv nodes.
*/
private boolean checkChangedCvValues(
int aspectIndex, StepControlAspect currentAspect, final Map cvNumberToNodeMap) {
// prepare the keyword to search
String keywordPolarity = String.format(StepControlKeywords.KEYWORD_PATTERN_POLARITY, aspectIndex);
CvNode polarityNode = StepControlCvUtils.getNode(mapKeywordToNode, keywordPolarity);
Polarity polarity = currentAspect.getPolarity();
boolean changeDetected =
CvValueUtils.hasChangedNewValue(polarityNode, Integer.toString(polarity.getCvValue()), cvNumberToNodeMap);
if (changeDetected) {
return true;
}
// the target position is a long type
String keywordPosition = String.format(StepControlKeywords.KEYWORD_PATTERN_POSITION, aspectIndex);
// get the master
LongCvNode positionNode = (LongCvNode) StepControlCvUtils.getNode(mapKeywordToNode, keywordPosition);
Long position = currentAspect.getPosition();
// prepare the master value
LOGGER.debug("The new position is: {}", position);
if (position != null) {
changeDetected = CvValueUtils.hasChangedNewValue(positionNode, Long.toString(position), cvNumberToNodeMap);
if (changeDetected) {
return true;
}
}
else {
LOGGER.warn("No position of aspect available. Current aspect: {}", currentAspect);
}
return false;
}
@Override
public void checkPendingChanges() {
boolean hasPendingChanges = hasPendingChanges();
if (!hasPendingChanges) {
resetPendingChanges();
}
}
private void resetPendingChanges() {
LOGGER.info("Reset pending changes.");
tabStatusListener.updatePendingChanges(component, false);
}
private boolean hasPendingChanges() {
// TODO implement hasPendingChanges()
final Map cvNumberToNodeMap = getCvNumberToNodeMap(selectedNode);
return checkHasPendingAspects(cvNumberToNodeMap);
}
@Override
public void writeConfigVariables(List cvList) {
fireWriteConfigVariables(cvList);
}
private void fireWriteConfigVariables(List cvList) {
// TODO decouple the AWT-thread from this work?
// add line
CvConsoleModel cvConsoleModel = CvConsoleModel.getConsoleModel();
for (ConfigurationVariable cv : cvList) {
cvConsoleModel
.addConsoleLine(ConsoleColor.black, String.format(">> CV %s : %s", cv.getName(), cv.getValue()));
}
for (CvDefinitionRequestListener l : cvDefinitionRequestListeners) {
l.writeCvValues(cvList);
}
}
private void fireWriteConfigVariablesPosition(List cvList) {
// TODO decouple the AWT-thread from this work?
LOGGER.info("Write cvValue for current position: {}", cvList);
Frame frame = JOptionPane.getFrameForComponent(getComponent());
try {
if (frame instanceof BusyFrame) {
((BusyFrame) frame).setBusy(true);
}
final Map cvNumberToNodeMap = getCvNumberToNodeMap(selectedNode);
final CvDefinitionPanelProvider cvDefinitionPanelProvider = StepControlPanel.this;
// write the values on the node
CvValueUtils.writeCvValues(selectedNode, cvList, cvNumberToNodeMap, cvDefinitionPanelProvider);
String currentPositionValue = null;
for (ConfigurationVariable configVar : cvList) {
LOGGER.info("Update the configVar in the model: {}", configVar);
selectedNode.getConfigVariables().put(configVar.getName(), configVar);
currentPositionValue = configVar.getValue();
}
try {
CvNode currentPositionNode =
StepControlCvUtils.getNode(cvNumberToNodeMap, StepControlKeywords.CVKEY_CURRENT_POSITION);
currentPositionNode.getConfigVar().setValue(currentPositionValue);
Long currentPosition = Long.parseLong(currentPositionValue);
LOGGER.info("Set the current position: {}", currentPosition);
stepControlModel.setCurrentPosition(currentPosition);
LOGGER
.info(
"The current position was read and no more CV values are updated! The current position is: {}",
currentPosition);
}
catch (Exception ex) {
LOGGER.warn("Update the current position failed.", ex);
}
}
finally {
if (frame instanceof BusyFrame) {
((BusyFrame) frame).setBusy(false);
}
}
}
private List fireLoadConfigVariablesPosition(List configVariables) {
// TODO decouple the AWT-thread from this work?
LOGGER.info("Load the config variables for the positions.");
for (CvDefinitionRequestListener l : cvDefinitionRequestListeners) {
return l.loadCvValues(configVariables);
}
return Collections.emptyList();
}
@Override
public void labelChanged(final MotorPort port, String label) {
}
@Override
public void valueChanged(final NodeInterface node, final MotorPort port) {
LOGGER.info("The motor port value has changed: {}", port);
// if (port.getId() == 0) {
// MotorPort motorPort = (MotorPort) port;
// motorSliderEditor.setValue(motorPort.getValue());
// }
}
@Override
public void configChanged(final NodeInterface node, final MotorPort port) {
}
@Override
public Class> getPortClass() {
return MotorPort.class;
}
private JPanel createFunctionButtonPanel(final JPanel parentPanel, final List functionButtons) {
FormBuilder formBuilder =
FormBuilder
.create()
.columns("pref, 8dlu, pref, 8dlu, pref, 8dlu, pref, 8dlu, pref, 8dlu, pref, 8dlu, pref, 8dlu, pref")
.rows("p, 3dlu, p").panel(parentPanel);
formBuilder.addSeparator(Resources.getString(getClass(), "additionalFunctions")).xyw(1, 1, 15);
int totalSoundPorts = 0;
if (selectedNode != null) {
Feature feature =
Feature
.findFeature(selectedNode.getNode().getFeatures(),
FeatureEnum.FEATURE_CTRL_SOUND_COUNT.getNumber());
if (feature != null) {
totalSoundPorts = feature.getValue();
}
}
LOGGER.info("Total number of sound ports: {}", totalSoundPorts);
// add the master sound button
final ImageIcon iconSoundMute =
ImageUtils.createImageIcon(StepControlPanel.class, "/icons/stepcontrol/sound_mute.png");
final ImageIcon iconSoundOn =
ImageUtils.createImageIcon(StepControlPanel.class, "/icons/stepcontrol/sound.png");
soundActiveButton = new JideToggleButton(Resources.getString(getClass(), "soundActive"));
soundActiveButton.setButtonStyle(JideButton.TOOLBOX_STYLE);
soundActiveButton.setSelectedIcon(iconSoundMute);
soundActiveButton.setIcon(iconSoundOn);
soundActiveButton.addActionListener(evt -> {
boolean isSoundMute = soundActiveButton.isSelected();
LOGGER.info("Pressed the sound active button, isSoundMute: {}", isSoundMute);
// activate / deactivate the sound
stepControlController.setSoundActive(!isSoundMute);
});
boolean soundActive = stepControlModel.isSoundActive();
soundActiveButton.setSelected(!soundActive);
formBuilder.add(soundActiveButton).xyw(1, 3, 5);
formBuilder.appendRows("10dlu, p");
// add the sound function buttons
final int columns = 8;
int currentSoundPortId = 0;
for (int row = 0; row < 4; row++) {
for (int column = 0; column < columns; column++) {
final int functionIndex = row * columns + (column);
final String buttonText = "F" + functionIndex;
final JideButton functionButton = new JideButton(buttonText);
functionButton.setButtonStyle(JideButton.TOOLBOX_STYLE);
functionButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
LOGGER.info("Trigger sound port with index: {}", functionIndex);
SoundPortStatus soundPortStatus = SoundPortStatus.PLAY;
stepControlController.triggerSoundPort(functionIndex, soundPortStatus);
}
});
functionButtons.add(functionButton);
functionButtonMap.put(buttonText, functionButton);
formBuilder.add(functionButton).xy((column * 2) + 1, (row * 2) + 5);
currentSoundPortId++;
if (currentSoundPortId >= totalSoundPorts) {
LOGGER.info("Maximum number of sound ports reached.");
break;
}
}
formBuilder.appendRows("3dlu, p");
if (currentSoundPortId >= totalSoundPorts) {
LOGGER.info("Maximum number of sound ports reached.");
break;
}
}
return formBuilder.build();
}
@Override
public void setActiveAspect(SwitchingNodeInterface node, int accessoryNumber, int aspectNumber) {
LOGGER.info("Set the active aspect: {}", aspectNumber);
if (SwingUtilities.isEventDispatchThread()) {
firePerformAspect(aspectNumber);
}
else {
try {
SwingUtilities.invokeAndWait(() -> firePerformAspect(aspectNumber));
}
catch (InvocationTargetException | InterruptedException ex) {
LOGGER
.warn("Activate aspect failed, accessoryNumber: {}, aspectNumber: {}", accessoryNumber,
aspectNumber, ex);
throw new RuntimeException(
"Activate aspect failed, accessoryNumber: " + accessoryNumber + ", aspect: " + aspectNumber);
}
}
}
@Override
public AccessoryExecutionState getAccessoryExecutionState(SwitchingNodeInterface node, int accessoryNumber) {
synchronized (aspectExecutionModel) {
return aspectExecutionModel.getExecutionState();
}
}
}