org.bidib.wizard.mvc.debug.view.DebugInterfaceView 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.debug.view;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Font;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.io.File;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.ListModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;
import org.bidib.jbidibc.debug.LineEndingEnum;
import org.bidib.jbidibc.messages.exception.InvalidLibraryException;
import org.bidib.jbidibc.messages.utils.ByteUtils;
import org.bidib.jbidibc.rxtx.PortIdentifierUtils;
import org.bidib.jbidibc.scm.ScmPortIdentifierUtils;
import org.bidib.wizard.api.locale.Resources;
import org.bidib.wizard.api.model.common.CommPort;
import org.bidib.wizard.client.common.component.TextLineNumber;
import org.bidib.wizard.client.common.text.WizardComponentFactory;
import org.bidib.wizard.client.common.view.BasicPopupMenu;
import org.bidib.wizard.client.common.view.DockKeys;
import org.bidib.wizard.common.model.settings.WizardSettingsInterface;
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.mvc.common.view.text.ChangeDocumentListener;
import org.bidib.wizard.mvc.common.view.text.CopyAllAction;
import org.bidib.wizard.mvc.common.view.text.HistoryModel;
import org.bidib.wizard.mvc.common.view.text.HistoryTextField;
import org.bidib.wizard.mvc.common.view.text.JTextFieldLimitDocument;
import org.bidib.wizard.mvc.debug.controller.listener.DebugInterfaceControllerListener;
import org.bidib.wizard.mvc.debug.model.DebugInterfaceModel;
import org.bidib.wizard.mvc.debug.view.listener.DebugInterfaceViewListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jgoodies.binding.adapter.Bindings;
import com.jgoodies.binding.adapter.ComboBoxAdapter;
import com.jgoodies.binding.beans.PropertyAdapter;
import com.jgoodies.binding.beans.PropertyConnector;
import com.jgoodies.binding.list.SelectionInList;
import com.jgoodies.binding.value.ConverterValueModel;
import com.jgoodies.binding.value.ValueModel;
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.swing.DefaultOverlayable;
import com.jidesoft.swing.JideScrollPane;
import com.jidesoft.swing.StyledLabelBuilder;
import com.vlsolutions.swing.docking.DockKey;
import com.vlsolutions.swing.docking.Dockable;
import com.vlsolutions.swing.docking.DockableState;
import com.vlsolutions.swing.docking.DockingDesktop;
import com.vlsolutions.swing.docking.event.DockableStateChangeEvent;
import com.vlsolutions.swing.docking.event.DockableStateChangeListener;
public class DebugInterfaceView implements Dockable {
private static final Logger LOGGER = LoggerFactory.getLogger(DebugInterfaceView.class);
private static final String ENCODED_DIALOG_COLUMN_SPECS =
"pref, 3dlu, max(100dlu;pref), 3dlu, 20dlu, 3dlu, pref, 3dlu, pref, 3dlu, pref, 3dlu, fill:pref:grow, 3dlu, pref, 3dlu, pref";
private static final String ENCODED_DIALOG_ROW_SPECS = "p, 3dlu, p, 3dlu, fill:50dlu:grow, 3dlu, p, 3dlu, p";
private final Collection listeners = new LinkedList<>();
private final DebugInterfaceModel debugInterfaceModel;
private final DockableStateChangeListener dockableStateChangeListener;
private final JComponent contentPanel;
private ValueModel selectionHolderComPort;
private ValueModel sendTextValueModel;
private ValueModel selectionHolderBaudRate;
private ValueModel sendFileValueModel;
private JTextField sendText;
private JTextField sendFile;
private final JButton refreshButton = new JButton(Resources.getString(getClass(), "refresh"));
private final JButton connectButton = new JButton(Resources.getString(getClass(), "connect"));
private final JButton disconnectButton = new JButton(Resources.getString(getClass(), "disconnect"));
private final JButton transmitButton = new JButton(Resources.getString(getClass(), "transmit"));
private final JButton transmitFileButton = new JButton(Resources.getString(getClass(), "transmit"));
private final JButton selectFileButton = new JButton(Resources.getString(getClass(), "selectFile"));
private JCheckBoxMenuItem autoScrollsItem;
private final JScrollPane logsPane;
private final JTextArea logsArea;
private TextLineNumber tln;
private SelectionInList commPortSelection;
private boolean timestampsEnabled;
private JCheckBox addTimeStamps;
private JCheckBox logToFile;
private ValueModel selectedLogFileValueModel;
private final JButton selectLogFileButton = new JButton(Resources.getString(getClass(), "select-logfile"));
private final SettingsService settingsService;
public DebugInterfaceView(final DockingDesktop desktop, final DebugInterfaceControllerListener listener,
final DebugInterfaceModel debugInterfaceModel, final SettingsService settingsService) {
this.settingsService = settingsService;
this.debugInterfaceModel = debugInterfaceModel;
DockKeys.DOCKKEY_DEBUG_INTERFACE_VIEW.setName(Resources.getString(getClass(), "title"));
DockKeys.DOCKKEY_DEBUG_INTERFACE_VIEW.setFloatEnabled(true);
DockKeys.DOCKKEY_DEBUG_INTERFACE_VIEW.setAutoHideEnabled(false);
this.dockableStateChangeListener = new DockableStateChangeListener() {
@Override
public void dockableStateChanged(final DockableStateChangeEvent event) {
LOGGER
.info("The state has changed, newState: {}, prevState: {}", event.getNewState(),
event.getPreviousState());
DockableState newState = event.getNewState();
if (newState.getDockable().equals(DebugInterfaceView.this) && newState.isClosed()) {
LOGGER.info("The DebugInterfaceView is closed.");
// we are closed
try {
desktop.removeDockableStateChangeListener(dockableStateChangeListener);
}
catch (Exception ex) {
LOGGER
.warn("Remove dockableStateChangeListener from desktop failed: "
+ dockableStateChangeListener, ex);
}
if (listener != null) {
LOGGER.info("Close the view.");
listener.viewClosed();
}
}
}
};
desktop.addDockableStateChangeListener(dockableStateChangeListener);
LOGGER.info("Create new DebugInterfaceView");
loadPortIdentifiers(this.debugInterfaceModel);
// create form builder
FormBuilder dialogBuilder = null;
boolean debugDialog = false;
if (debugDialog) {
JPanel panel = new FormDebugPanel();
dialogBuilder =
FormBuilder.create().columns(ENCODED_DIALOG_COLUMN_SPECS).rows(ENCODED_DIALOG_ROW_SPECS).panel(panel);
}
else {
JPanel panel = new JPanel(new BorderLayout());
dialogBuilder =
FormBuilder.create().columns(ENCODED_DIALOG_COLUMN_SPECS).rows(ENCODED_DIALOG_ROW_SPECS).panel(panel);
}
dialogBuilder.border(Paddings.DIALOG);
// add some components
commPortSelection =
new SelectionInList((ListModel) this.debugInterfaceModel.getCommPortsListModel());
selectionHolderComPort =
new PropertyAdapter(this.debugInterfaceModel,
DebugInterfaceModel.PROPERTY_SELECTED_PORT, true);
ComboBoxAdapter comboBoxAdapterCommPorts =
new ComboBoxAdapter(commPortSelection, selectionHolderComPort);
JComboBox comboCommPorts = new JComboBox();
comboCommPorts.setModel(comboBoxAdapterCommPorts);
if (this.debugInterfaceModel.getSelectedPort() != null) {
comboCommPorts.setSelectedItem(this.debugInterfaceModel.getSelectedPort());
}
dialogBuilder.add(Resources.getString(getClass(), "selectedPort")).xy(1, 1);
dialogBuilder.add(comboCommPorts).xy(3, 1);
refreshButton.setBorder(new EmptyBorder(5, 5, 5, 5));
dialogBuilder.add(refreshButton).xy(5, 1);
List baudRates = new ArrayList<>();
baudRates.add(Integer.valueOf(19200));
baudRates.add(Integer.valueOf(115200));
selectionHolderBaudRate =
new PropertyAdapter(this.debugInterfaceModel, DebugInterfaceModel.PROPERTY_BAUDRATE,
true);
ComboBoxAdapter comboBoxAdapter = new ComboBoxAdapter(baudRates, selectionHolderBaudRate);
JComboBox comboBaudRate = new JComboBox<>();
comboBaudRate.setModel(comboBoxAdapter);
comboBaudRate.setSelectedIndex(1);
dialogBuilder.add(Resources.getString(getClass(), "selectedBaudRate")).xy(7, 1);
dialogBuilder.add(comboBaudRate).xy(9, 1);
refreshButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fireRefreshComPorts();
}
});
connectButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fireConnect();
}
});
disconnectButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fireDisconnect();
}
});
disconnectButton.setEnabled(false);
List lineEndings = new ArrayList<>();
for (LineEndingEnum lineEnding : LineEndingEnum.values()) {
lineEndings.add(lineEnding);
}
// SelectionInList lineEndingSelection = new SelectionInList<>(lineEndings);
ValueModel selectionHolderLineEnding =
new PropertyAdapter(this.debugInterfaceModel, DebugInterfaceModel.PROPERTY_LINE_ENDING,
true);
ComboBoxAdapter comboAdapterLineEnding =
new ComboBoxAdapter(lineEndings, selectionHolderLineEnding);
JComboBox comboLineEnding = new JComboBox<>();
comboLineEnding.setModel(comboAdapterLineEnding);
comboLineEnding.setRenderer(new LineEndingCellRenderer());
// prepare the connect and disconnect button
JPanel debugInterfaceActionButtons =
new ButtonBarBuilder().addButton(connectButton).addRelatedGap().addButton(disconnectButton).build();
dialogBuilder.add(debugInterfaceActionButtons).xy(11, 1);
addTimeStamps = new JCheckBox(Resources.getString(getClass(), "addTimestamps"));
dialogBuilder.add(addTimeStamps).xy(13, 1);
// dialogBuilder.appendRows("p");
logToFile = new JCheckBox(Resources.getString(getClass(), "logToFile"));
dialogBuilder.add(logToFile).xy(1, 3);
selectedLogFileValueModel =
new PropertyAdapter(this.debugInterfaceModel,
DebugInterfaceModel.PROPERTY_LOGFILE_NAME, true);
JTextField selectedLogFileText = WizardComponentFactory.createTextField(selectedLogFileValueModel, true);
selectedLogFileText.setEditable(false);
dialogBuilder.add(selectedLogFileText).xy(3, 3);
// prepare the select and save button
selectLogFileButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
selectLogFile();
}
});
dialogBuilder.add(selectLogFileButton).xy(5, 3);
dialogBuilder.add(Resources.getString(getClass(), "lineEnding")).xy(7, 3);
dialogBuilder.add(comboLineEnding).xy(9, 3);
// add the log area
this.logsPane = new JScrollPane() {
private static final long serialVersionUID = 1L;
@Override
protected void processMouseWheelEvent(MouseWheelEvent e) {
super.processMouseWheelEvent(e);
if (logsArea.getAutoscrolls()) {
LOGGER.info("Clear the flag on the autoScrollsItem and disable autoscrolls.");
// clear the flag on the autoScrollsItem
autoScrollsItem.setSelected(false);
logsArea.setAutoscrolls(false);
logsArea.setCaretPosition(logsArea.getCaretPosition() > 0 ? logsArea.getCaretPosition() - 1 : 0);
}
}
};
this.logsArea = new JTextArea() {
private static final long serialVersionUID = 1L;
@Override
protected void processMouseEvent(MouseEvent e) {
if (getAutoscrolls() && e.getID() == MouseEvent.MOUSE_RELEASED && !e.isPopupTrigger()) {
LOGGER.info("Clear the flag on the autoScrollsItem.");
// clear the flag on the autoScrollsItem
autoScrollsItem.setSelected(false);
}
super.processMouseEvent(e);
}
};
this.logsArea.setEditable(false);
this.logsArea.setFont(new Font("Monospaced", Font.PLAIN, 13));
logsPane.getViewport().add(logsArea, null);
// JScrollPane logsPane = new JScrollPane(this.logsArea);
tln = new TextLineNumber(this.logsArea);
logsPane.setRowHeaderView(tln);
logsPane.setAutoscrolls(true);
dialogBuilder.add(logsPane).xyw(1, 5, 17);
// create the textfield for send message to debug
sendTextValueModel =
new PropertyAdapter(this.debugInterfaceModel, DebugInterfaceModel.PROPERTY_SEND_TEXT,
true);
// set default 20 items in history
HistoryModel.setMax(20);
sendText = new HistoryTextField("sendText", false, true);
final Document sendTextDoc = new JTextFieldLimitDocument(128);
sendText.setDocument(sendTextDoc);
Bindings.bind(sendText, sendTextValueModel, false);
sendText.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fireTransmit();
}
});
final DefaultOverlayable sendTextOverlayable = new DefaultOverlayable(sendText);
sendTextOverlayable
.addOverlayComponent(
StyledLabelBuilder
.createStyledLabel("{" + Resources.getString(getClass(), "transmitText.prompt") + ":f:gray}"),
SwingConstants.WEST);
sendTextOverlayable.setOverlayLocationInsets(new Insets(0, -5, 0, 5));
// final Document sendTextDoc = sendText.getDocument();
sendTextDoc
.addDocumentListener(
new ChangeDocumentListener(doc -> sendTextOverlayable.setOverlayVisible(doc.getLength() < 1)));
sendTextOverlayable.setOverlayVisible(sendTextDoc.getLength() < 1);
dialogBuilder.add(Resources.getString(getClass(), "transmitText")).xy(1, 7);
dialogBuilder.add(sendTextOverlayable).xyw(3, 7, 13);
dialogBuilder.add(transmitButton).xy(17, 7);
transmitButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fireTransmit();
}
});
transmitButton.setEnabled(false);
// create the textfield for filename
sendFileValueModel =
new PropertyAdapter(this.debugInterfaceModel, DebugInterfaceModel.PROPERTY_SEND_FILE,
true);
final ValueModel sendFileConverterModel =
new ConverterValueModel(sendFileValueModel, new FileStringConverter());
sendFile = WizardComponentFactory.createTextField(sendFileConverterModel, true);
sendFile.setEditable(false);
final DefaultOverlayable sendFileOverlayable = new DefaultOverlayable(sendFile);
sendFileOverlayable
.addOverlayComponent(
StyledLabelBuilder
.createStyledLabel("{" + Resources.getString(getClass(), "transmitFile.prompt") + ":f:gray}"),
SwingConstants.WEST);
sendFileOverlayable.setOverlayLocationInsets(new Insets(0, -5, 0, 5));
final Document sendFileDoc = sendFile.getDocument();
sendFileDoc
.addDocumentListener(
new ChangeDocumentListener(doc -> sendFileOverlayable.setOverlayVisible(doc.getLength() < 1)));
sendFileOverlayable.setOverlayVisible(sendFileDoc.getLength() < 1);
dialogBuilder.add(Resources.getString(getClass(), "transmitFile")).xy(1, 9);
dialogBuilder.add(sendFileOverlayable).xyw(3, 9, 11);
dialogBuilder.add(selectFileButton).xy(15, 9);
selectFileButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fireSelectFile();
}
});
dialogBuilder.add(transmitFileButton).xy(17, 9);
transmitFileButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fireTransmitFile();
}
});
transmitFileButton.setEnabled(false);
// add bindings for enable/disable the send button
PropertyConnector
.connect(this.debugInterfaceModel, DebugInterfaceModel.PROPERTY_TRANSMIT_ENABLED, transmitButton,
"enabled");
PropertyConnector
.connect(this.debugInterfaceModel, DebugInterfaceModel.PROPERTY_TRANSMIT_ENABLED, transmitFileButton,
"enabled");
PropertyConnector
.connect(this.debugInterfaceModel, DebugInterfaceModel.PROPERTY_DISCONNECTED, connectButton, "enabled");
PropertyConnector
.connect(this.debugInterfaceModel, DebugInterfaceModel.PROPERTY_CONNECTED, disconnectButton, "enabled");
JPanel contentPanelTemp = dialogBuilder.build();
JideScrollPane scrollPane = new JideScrollPane(contentPanelTemp);
contentPanel = scrollPane;
final JPopupMenu popupMenu = new BasicPopupMenu();
prepareMenuItems(popupMenu);
logsArea.setComponentPopupMenu(popupMenu);
addTimeStamps.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
final boolean timestampsEnabled = e.getStateChange() == ItemEvent.SELECTED;
fireTimestampsEnabledChanged(timestampsEnabled);
}
});
logToFile.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
final boolean logToFileEnabled = e.getStateChange() == ItemEvent.SELECTED;
DebugInterfaceView.this.debugInterfaceModel.setLogToFile(logToFileEnabled);
}
});
}
private void prepareMenuItems(JPopupMenu menu) {
autoScrollsItem = new JCheckBoxMenuItem(Resources.getString(getClass(), "autoScrolls"));
autoScrollsItem.setSelected(true);
autoScrollsItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
logsArea.setCaretPosition(logsArea.getDocument().getLength());
boolean enabled = autoScrollsItem.isSelected();
LOGGER.info("Set autoscrolls enabled: {}", enabled);
if (enabled) {
logsArea.setAutoscrolls(enabled);
logsArea.setCaretPosition(logsArea.getDocument().getLength());
}
else {
logsArea.setAutoscrolls(enabled);
logsArea.setCaretPosition(logsArea.getCaretPosition() > 0 ? logsArea.getCaretPosition() - 1 : 0);
}
}
});
addMenuItem(menu, autoScrollsItem);
JMenuItem clearConsole = new JMenuItem(Resources.getString(getClass(), "clear_console"));
clearConsole.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fireClearConsole();
}
});
addMenuItem(menu, clearConsole);
JMenuItem copyAllToClipboard = new JMenuItem(Resources.getString(getClass(), "copyAllToClipboard"));
copyAllToClipboard.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fireCopyAllToClipboard();
}
});
addMenuItem(menu, copyAllToClipboard);
JMenuItem saveToFile =
new JMenuItem(Resources.getString(getClass(), "save_to_file"),
ImageUtils.createImageIcon(getClass(), "/icons/savetofile.png"));
saveToFile.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fireSaveToFile(false);
}
});
addMenuItem(menu, saveToFile);
JMenuItem saveSelectedToFile =
new JMenuItem(Resources.getString(getClass(), "save_selected_to_file"),
ImageUtils.createImageIcon(getClass(), "/icons/saveselectedtofile.png"));
saveSelectedToFile.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fireSaveToFile(true);
}
});
addMenuItem(menu, saveSelectedToFile);
}
private void addMenuItem(Object menu, JMenuItem menuItem) {
if (menu instanceof JMenu) {
((JMenu) menu).add(menuItem);
}
else if (menu instanceof JPopupMenu) {
((JPopupMenu) menu).add(menuItem);
}
}
private void fireTimestampsEnabledChanged(boolean timestampsEnabled) {
this.timestampsEnabled = timestampsEnabled;
}
private void fireClearConsole() {
LOGGER.info("clear the console.");
logsArea.setText(null);
logsArea.setCaretPosition(logsArea.getDocument().getLength());
boolean enabled = autoScrollsItem.isSelected();
logsArea.setAutoscrolls(!enabled);
logsArea.setAutoscrolls(enabled);
}
private void fireCopyAllToClipboard() {
LOGGER.info("Copy all content to clipboard.");
ActionEvent copyAll = new ActionEvent(logsArea, 0, CopyAllAction.copyAllAction);
CopyAllAction action = new CopyAllAction();
action.actionPerformed(copyAll);
}
private static FileFilter logfileFilter;
private static final String LOGFILE_EXTENSION = "log";
private static final String WORKING_DIR_DEBUG_INTERFACE_KEY = "debugInterface";
// description, suffix for node files
private String savedLogFilesDescription;
private void fireSaveToFile(final boolean selectedOnly) {
LOGGER.info("Save the console content to file.");
savedLogFilesDescription = Resources.getString(getClass(), "savedLogFilesDescription");
logfileFilter = new FileNameExtensionFilter(savedLogFilesDescription, LOGFILE_EXTENSION);
final WizardSettingsInterface wizardSettings = settingsService.getWizardSettings();
String storedWorkingDirectory = wizardSettings.getWorkingDirectory(WORKING_DIR_DEBUG_INTERFACE_KEY);
FileDialog dialog = new FileDialog(logsArea, FileDialog.SAVE, storedWorkingDirectory, null, logfileFilter) {
@Override
public void approve(final String fileName) {
try {
// setWaitCursor();
LOGGER.info("Start saving logfile, fileName: {}", fileName);
File file = new File(fileName);
if (selectedOnly) {
FileUtils.write(file, logsArea.getSelectedText(), Charset.forName("UTF-8"));
}
else {
FileUtils.write(file, logsArea.getText(), Charset.forName("UTF-8"));
}
final String workingDir = Paths.get(fileName).getParent().toString();
LOGGER.info("Save current workingDir: {}", workingDir);
wizardSettings.setWorkingDirectory(WORKING_DIR_DEBUG_INTERFACE_KEY, workingDir);
}
catch (Exception ex) {
LOGGER.warn("Save logfile failed.", ex);
throw new RuntimeException("Save logfile failed.");
}
finally {
// setDefaultCursor();
}
}
};
dialog.showDialog();
}
private void fireRefreshComPorts() {
// Reload the com port identifiers
loadPortIdentifiers(debugInterfaceModel);
}
private void fireConnect() {
for (DebugInterfaceViewListener listener : listeners) {
listener.openConnection();
}
}
private void fireDisconnect() {
for (DebugInterfaceViewListener listener : listeners) {
listener.closeConnection();
}
}
private void fireTransmit() {
for (DebugInterfaceViewListener listener : listeners) {
listener.transmit();
}
// clear the text field
sendText.setText(null);
}
private void fireTransmitFile() {
boolean modal = true;
// open the progress dialog
FileTransferProgressDialog progressDialog = new FileTransferProgressDialog(contentPanel, modal, listeners);
}
private void fireSelectFile() {
final WizardSettingsInterface wizardSettings = settingsService.getWizardSettings();
String storedWorkingDirectory = wizardSettings.getWorkingDirectory(WORKING_DIR_DEBUG_INTERFACE_KEY);
final FileDialog dialog = new FileDialog(contentPanel, FileDialog.OPEN, storedWorkingDirectory, null, ff) {
@Override
public void approve(final String selectedFile) {
File file = new File(selectedFile);
debugInterfaceModel.setSendFile(file);
final String workingDir = Paths.get(selectedFile).getParent().toString();
LOGGER.info("Save current workingDir: {}", workingDir);
wizardSettings.setWorkingDirectory(WORKING_DIR_DEBUG_INTERFACE_KEY, workingDir);
}
};
dialog.showDialog();
}
@Override
public DockKey getDockKey() {
return DockKeys.DOCKKEY_DEBUG_INTERFACE_VIEW;
}
@Override
public Component getComponent() {
return contentPanel;
}
public void addDebugInterfaceViewListener(DebugInterfaceViewListener l) {
listeners.add(l);
}
private int currentLineLen;
public void addLog(final String logMessage) {
SwingUtilities.invokeLater(() -> addLogMessage(logMessage));
}
private final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
public static final String LOGGER_PANE_NAME = "DebugInterfacePane";
private static final Logger LOGGER_PANE = LoggerFactory.getLogger(LOGGER_PANE_NAME);
private final StringBuilder sbLogger = new StringBuilder();
private void addLogMessage(final String logMessage) {
try {
int lines = logsArea.getLineCount();
if (lines > 2000) {
// remove the first 50 lines
int end = logsArea.getLineEndOffset(/* lines - */50);
logsArea.getDocument().remove(0, end);
}
}
catch (BadLocationException ex) {
LOGGER.warn("Remove some lines from logsArea failed.", ex);
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Received message: {}", ByteUtils.bytesToHex(logMessage.getBytes(StandardCharsets.UTF_8)));
}
// ...........\n........
int beginIndex = 0;
int index = logMessage.indexOf('\n');
int lenOfTimestamp = 0;
if (index > -1) {
// check if the line contains only leading 00
if ((index - beginIndex) == 2 && logMessage.charAt(0) == 0x00) {
LOGGER.warn("Skip empty line with 00 0D 0A.");
logsArea.append("\n");
beginIndex = index + 1;
index = logMessage.indexOf('\n', beginIndex);
}
// found line terminator
while (index > -1) {
// print the whole line
String part = logMessage.substring(beginIndex, index);
// add text to scroll pane
if (timestampsEnabled && currentLineLen == 0) {
// add the timestamp to the line
sbLogger.append(sdf.format(new Date()));
sbLogger.append(" - ");
lenOfTimestamp = 12;
}
sbLogger.append(part);
LOGGER.debug("1. Added part: {}, currentLineLen: {}, sbLogger: {}", part, currentLineLen, sbLogger);
// the line is complete
logsArea.append(sbLogger.substring(currentLineLen));
logsArea.append("\n"/* System.lineSeparator() */);
LOGGER_PANE.info(sbLogger.toString().replace("\r", ""));
lenOfTimestamp = 0;
sbLogger.setLength(0);
currentLineLen = 0;
beginIndex = index + 1;
index = logMessage.indexOf('\n', beginIndex);
}
lenOfTimestamp = 0;
if (beginIndex < logMessage.length()) {
// add text to scroll pane
String part = logMessage.substring(beginIndex);
if (timestampsEnabled) {
sbLogger.append(sdf.format(new Date()));
sbLogger.append(" - ");
lenOfTimestamp = 12;
}
sbLogger.append(part);
LOGGER.debug("2. Added part: {}, currentLineLen: {}, sbLogger: {}", part, currentLineLen, sbLogger);
logsArea.append(sbLogger.substring(currentLineLen));
currentLineLen = part.length() + lenOfTimestamp;
lenOfTimestamp = 0;
LOGGER.debug("Added last part: {}, currentLineLen: {}", part, currentLineLen);
}
}
else {
// add text to scroll pane
if (timestampsEnabled && currentLineLen == 0) {
LOGGER.debug("Add timestamp, currentLineLen: {}", currentLineLen);
sbLogger.append(sdf.format(new Date()));
sbLogger.append(" - ");
lenOfTimestamp = 12;
// currentLineLen += lenOfTimestamp;
}
String toLog = logMessage;
// split the received data if necessary
if (logMessage.length() > (120 - currentLineLen)) {
int start = 0;
int end = 0;
String part = null;
toLog = null;
end = start + (120 - currentLineLen);
while (end < logMessage.length()) {
// end = start + (120 - currentLineLen);
part = StringUtils.substring(logMessage, start, end);
LOGGER.debug("Fetched part, currentLineLen {}, part: '{}'", currentLineLen, part);
sbLogger.append(part);
logsArea.append(sbLogger.substring(currentLineLen));
logsArea.append("\n");
currentLineLen = 120;
LOGGER.debug("Added logMessage: {}, currentLineLen: {}", logMessage, currentLineLen);
currentLineLen = 0;
sbLogger.setLength(0);
start += 120;
end = start + (120 - currentLineLen);
}
if (start < logMessage.length()) {
LOGGER.debug("Keep the remaining part from logMessage, currentLineLen: {}", currentLineLen, start);
toLog = StringUtils.substring(logMessage, start);
}
}
if (StringUtils.isNotBlank(toLog)) {
if (toLog.charAt(0) == 0x00 && toLog.length() == 1) {
LOGGER.warn("Skip empty line with 00.");
}
else {
sbLogger.append(toLog);
LOGGER
.debug("3. Added logMessage: {}, currentLineLen: {}, sbLogger: {}", toLog, currentLineLen,
sbLogger);
logsArea.append(sbLogger.substring(currentLineLen));
currentLineLen += toLog.length() + lenOfTimestamp;
LOGGER.debug("Added logMessage: {}, currentLineLen: {}", logMessage, currentLineLen);
}
}
}
// Update and scroll pane to the bottom
if (currentLineLen > 120) {
LOGGER.debug("Append new line to logsArea, currentLineLen: {}", currentLineLen);
// break the line
logsArea.append("\n");
LOGGER_PANE.info(sbLogger.toString().replace("\r", ""));
lenOfTimestamp = 0;
sbLogger.setLength(0);
currentLineLen = 0;
}
logsArea.invalidate();
}
private static final String SUFFIX_HEX = "hex";
private static final String SUFFIX_EEP = "eep";
private final FileFilter ff = new FileFilter() {
@Override
public boolean accept(File file) {
boolean result = false;
if (file != null) {
if (file.isDirectory()) {
result = true;
}
else if (FilenameUtils.wildcardMatch(file.getName(), "*." + SUFFIX_HEX)) {
result = true;
}
else if (FilenameUtils.wildcardMatch(file.getName(), "*." + SUFFIX_EEP)) {
result = true;
}
}
return result;
}
@Override
public String getDescription() {
return Resources.getString(DebugInterfaceView.class, "filter") + " (*." + SUFFIX_HEX + ",*." + SUFFIX_EEP
+ ")";
}
};
private void loadPortIdentifiers(DebugInterfaceModel model) {
LOGGER.info("Load the comm ports, model: {}", model);
Set commPorts = new HashSet();
try {
// use PortIdentifierUtils because we must load the RXTX libraries
List portIdentifiers = null;
switch (settingsService.getMiscSettings().getSelectedSerialPortProvider()) {
case "SCM":
portIdentifiers = ScmPortIdentifierUtils.getPortIdentifiers();
break;
case "SPSW":
portIdentifiers = org.bidib.jbidibc.purejavacomm.PortIdentifierUtils.getPortIdentifiers();
break;
case "JSerialComm":
portIdentifiers = org.bidib.jbidibc.jserialcomm.PortIdentifierUtils.getPortIdentifiers();
break;
case "PureJavaComm":
portIdentifiers = org.bidib.jbidibc.purejavacomm.PortIdentifierUtils.getPortIdentifiers();
break;
default:
portIdentifiers = PortIdentifierUtils.getPortIdentifiers();
break;
}
if (portIdentifiers != null) {
for (String id : portIdentifiers) {
LOGGER.info("Add new CommPort with id: {}", id);
commPorts.add(new CommPort(id));
}
}
}
catch (InvalidLibraryException ex) {
LOGGER
.warn(
"Fetch port identifiers failed. This can be caused because the ext/lib directory of the Java installation contains an old RXTXComm.jar!",
ex);
JOptionPane
.showMessageDialog(contentPanel,
Resources
.getString(DebugInterfaceView.class, "fetch-port-identifiers-failed",
new Object[] { new File(SystemUtils.getJavaHome(), "lib/ext").getPath() }),
Resources.getString(DebugInterfaceView.class, "title-error"), JOptionPane.ERROR_MESSAGE);
}
catch (Exception ex) {
LOGGER.warn("Fetch port identifiers failed.", ex);
}
model.setCommPorts(Arrays.asList(commPorts.toArray(new CommPort[0])));
}
private class LineEndingCellRenderer extends DefaultListCellRenderer {
private static final long serialVersionUID = 1L;
private Map labelMap = new HashMap<>();
public LineEndingCellRenderer() {
for (LineEndingEnum lineEndingEnum : LineEndingEnum.values()) {
String label = Resources.getString(LineEndingEnum.class, lineEndingEnum.getKey());
labelMap.put(lineEndingEnum.getKey(), label);
}
}
@Override
public Component getListCellRendererComponent(
JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
JLabel renderer = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (value instanceof LineEndingEnum) {
LineEndingEnum lineEndingEnum = (LineEndingEnum) value;
renderer.setText(labelMap.get(lineEndingEnum.getKey()));
}
else {
renderer.setText(null);
}
return renderer;
}
}
private static final String SUFFIX_LOG = "log";
private static final String SUFFIX_TXT = "txt";
private final FileFilter ffLogFile = new FileFilter() {
@Override
public boolean accept(File file) {
boolean result = false;
if (file != null) {
if (file.isDirectory()) {
result = true;
}
else if (FilenameUtils.wildcardMatch(file.getName(), "*." + SUFFIX_LOG)) {
result = true;
}
else if (FilenameUtils.wildcardMatch(file.getName(), "*." + SUFFIX_TXT)) {
result = true;
}
}
return result;
}
@Override
public String getDescription() {
return Resources.getString(DebugInterfaceView.class, "filterLogFile") + " (*." + SUFFIX_LOG + ",*."
+ SUFFIX_TXT + ")";
}
};
private void selectLogFile() {
final WizardSettingsInterface wizardSettings = settingsService.getWizardSettings();
String storedWorkingDirectory = wizardSettings.getWorkingDirectory(WORKING_DIR_DEBUG_INTERFACE_KEY);
final FileDialog dialog =
new FileDialog(contentPanel, FileDialog.SAVE, storedWorkingDirectory, null, ffLogFile) {
@Override
public void approve(final String selectedFile) {
File file = new File(selectedFile);
debugInterfaceModel.setLogFileName(file.toString());
final String workingDir = Paths.get(selectedFile).getParent().toString();
LOGGER.info("Save current workingDir: {}", workingDir);
wizardSettings.setWorkingDirectory(WORKING_DIR_DEBUG_INTERFACE_KEY, workingDir);
}
};
dialog.showDialog();
}
}