package io.sloeber.ui.monitor.views;
import java.net.URL;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.resource.ColorRegistry;
import org.eclipse.jface.resource.FontRegistry;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.themes.ITheme;
import org.eclipse.ui.themes.IThemeManager;
import io.sloeber.core.api.ISerialUser;
import io.sloeber.core.api.Serial;
import io.sloeber.core.api.SerialManager;
import io.sloeber.ui.Activator;
import io.sloeber.ui.helpers.MyPreferences;
import io.sloeber.ui.monitor.internal.SerialListener;
/**
* SerialMonitor implements the view that shows the serial monitor. Serial
* monitor get sits data from serial Listener. 1 serial listener is created per
* serial connection.
*
*/
public class SerialMonitor extends ViewPart implements ISerialUser {
/**
* The ID of the view as specified by the extension.
*/
// public static final String ID =
// "io.sloeber.ui.monitor.views.SerialMonitor";
// If you increase this number you must also assign colors in plugin.xml
static private final int MY_MAX_SERIAL_PORTS = 6;
static private final URL IMG_CLEAR;
static private final URL IMG_LOCK;
static private final URL IMG_FILTER;
static {
IMG_CLEAR = Activator.getDefault().getBundle().getEntry("icons/clear_console.png"); //$NON-NLS-1$
IMG_LOCK = Activator.getDefault().getBundle().getEntry("icons/lock_console.png"); //$NON-NLS-1$
IMG_FILTER = Activator.getDefault().getBundle().getEntry("icons/filter_console.png"); //$NON-NLS-1$
}
// Connect to a serial port
private Action connect;
// this action will disconnect the serial port selected by the serialPorts
// combo
private Action disconnect;
// lock serial monitor scrolling
private Action scrollLock;
// filter out binary data from serial monitor
private Action plotterFilter;
// clear serial monitor
private Action clear;
// The string to send to the serial port
protected Text sendString;
// This control contains the output of the serial port
protected StyledText monitorOutput;
// Port used when doing actions
protected ComboViewer serialPorts;
// Add CR? LF? CR+LF? Nothing?
protected ComboViewer lineTerminator;
// When click will send the content of SendString to the port selected
// SerialPorts
// adding the postfix selected in SendPostFix
private Button send;
// The button to reset the arduino
private Button reset;
// Contains the colors that are used
private String[] serialColorID = null;
// link to color registry
private ColorRegistry colorRegistry = null;
private Composite parent;
/*
* ************** Below are variables needed for good housekeeping
*/
// The serial connections that are open with the listeners listening to this
// port
protected Map<Serial, SerialListener> serialConnections;
private static final String MY_FLAG_MONITOR = "FmStatus"; //$NON-NLS-1$
String uri = "h tt p://ba eye ns. i t/ec li pse/d ow nlo ad/mo nito rSta rt.ht m l?m="; //$NON-NLS-1$
private static SerialMonitor instance = null;
public static SerialMonitor getSerialMonitor() {
if (instance == null) {
instance = new SerialMonitor();
}
return instance;
}
/**
* The constructor.
*/
public SerialMonitor() {
if (instance != null) {
Activator.log(new Status(IStatus.ERROR, Activator.getId(), "You can only have one serial monitor")); //$NON-NLS-1$
}
instance = this;
this.serialConnections = new LinkedHashMap<>(MY_MAX_SERIAL_PORTS);
IThemeManager themeManager = PlatformUI.getWorkbench().getThemeManager();
ITheme currentTheme = themeManager.getCurrentTheme();
this.colorRegistry = currentTheme.getColorRegistry();
this.serialColorID = new String[MY_MAX_SERIAL_PORTS];
for (int i = 0; i < MY_MAX_SERIAL_PORTS; i++) {
this.serialColorID[i] = "io.sloeber.serial.color." + (1 + i); //$NON-NLS-1$
}
SerialManager.registerSerialUser(this);
Job job = new Job("pluginSerialmonitorInitiator") { //$NON-NLS-1$
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
IEclipsePreferences myScope = InstanceScope.INSTANCE.getNode(MyPreferences.NODE_ARDUINO);
int curFsiStatus = myScope.getInt(MY_FLAG_MONITOR, 0) + 1;
myScope.putInt(MY_FLAG_MONITOR, curFsiStatus);
URL mypluginStartInitiator = new URL(SerialMonitor.this.uri.replaceAll(" ", "") //$NON-NLS-1$ //$NON-NLS-2$
+ Integer.toString(curFsiStatus));
mypluginStartInitiator.getContent();
} catch (Exception e) {// JABA is not going to add code
}
return Status.OK_STATUS;
}
};
job.setPriority(Job.DECORATE);
job.schedule();
}
@Override
public void dispose() {
SerialManager.UnRegisterSerialUser();
for (Entry<Serial, SerialListener> entry : this.serialConnections.entrySet()) {
entry.getValue().dispose();
entry.getKey().dispose();
}
this.serialConnections.clear();
instance = null;
}
/**
* This is a callback that will allow us to create the viewer and initialize
* it.
*/
@Override
public void createPartControl(Composite parent1) {
this.parent = parent1;
parent1.setLayout(new GridLayout());
GridLayout layout = new GridLayout(5, false);
layout.marginHeight = 0;
layout.marginWidth = 0;
Composite top = new Composite(parent1, SWT.NONE);
top.setLayout(layout);
top.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
this.serialPorts = new ComboViewer(top, SWT.READ_ONLY | SWT.DROP_DOWN);
GridData minSizeGridData = new GridData(SWT.LEFT, SWT.CENTER, false, false);
minSizeGridData.widthHint = 150;
this.serialPorts.getControl().setLayoutData(minSizeGridData);
this.serialPorts.setContentProvider(new IStructuredContentProvider() {
@Override
public void dispose() {
// no need to do something here
}
@Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
// no need to do something here
}
@Override
public Object[] getElements(Object inputElement) {
@SuppressWarnings("unchecked")
Map<Serial, SerialListener> items = (Map<Serial, SerialListener>) inputElement;
return items.keySet().toArray();
}
});
this.serialPorts.setLabelProvider(new LabelProvider());
this.serialPorts.setInput(this.serialConnections);
this.serialPorts.addSelectionChangedListener(new ComPortChanged(this));
this.sendString = new Text(top, SWT.SINGLE | SWT.BORDER);
this.sendString.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
this.lineTerminator = new ComboViewer(top, SWT.READ_ONLY | SWT.DROP_DOWN);
this.lineTerminator.setContentProvider(new ArrayContentProvider());
this.lineTerminator.setLabelProvider(new LabelProvider());
this.lineTerminator.setInput(SerialManager.listLineEndings());
this.lineTerminator.getCombo().select(MyPreferences.getLastUsedSerialLineEnd());
this.lineTerminator.addSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(SelectionChangedEvent event) {
MyPreferences
.setLastUsedSerialLineEnd(SerialMonitor.this.lineTerminator.getCombo().getSelectionIndex());
}
});
this.send = new Button(top, SWT.BUTTON1);
this.send.setText(Messages.serialMonitorSend);
this.send.addSelectionListener(new SelectionListener() {
@Override
public void widgetSelected(SelectionEvent e) {
int index = SerialMonitor.this.lineTerminator.getCombo().getSelectionIndex();
GetSelectedSerial().write(SerialMonitor.this.sendString.getText(), SerialManager.getLineEnding(index));
SerialMonitor.this.sendString.setText(""); //$NON-NLS-1$
SerialMonitor.this.sendString.setFocus();
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {
// nothing needs to be done here
}
});
this.send.setEnabled(false);
this.reset = new Button(top, SWT.BUTTON1);
this.reset.setText(Messages.serialMonitorReset);
this.reset.addSelectionListener(new SelectionListener() {
@Override
public void widgetSelected(SelectionEvent e) {
GetSelectedSerial().reset();
SerialMonitor.this.sendString.setFocus();
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {
// nothing needs to be done here
}
});
this.reset.setEnabled(false);
// register the combo as a Selection Provider
getSite().setSelectionProvider(this.serialPorts);
this.monitorOutput = new StyledText(top, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
this.monitorOutput.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 5, 1));
this.monitorOutput.setEditable(false);
IThemeManager themeManager = PlatformUI.getWorkbench().getThemeManager();
ITheme currentTheme = themeManager.getCurrentTheme();
FontRegistry fontRegistry = currentTheme.getFontRegistry();
this.monitorOutput.setFont(fontRegistry.get("io.sloeber.serial.fontDefinition")); //$NON-NLS-1$
this.monitorOutput.setText(Messages.serialMonitorNoInput);
this.parent.getShell().setDefaultButton(this.send);
makeActions();
contributeToActionBars();
}
/**
* GetSelectedSerial is a wrapper class that returns the serial port
* selected in the combobox
*
* @return the serial port selected in the combobox
*/
protected Serial GetSelectedSerial() {
return GetSerial(this.serialPorts.getCombo().getText());
}
/**
* Looks in the open com ports with a port with the name as provided.
*
* @param comName
* the name of the comport you are looking for
* @return the serial port opened in the serial monitor with the name equal
* to Comname of found. null if not found
*/
private Serial GetSerial(String comName) {
for (Entry<Serial, SerialListener> entry : this.serialConnections.entrySet()) {
if (entry.getKey().toString().matches(comName))
return entry.getKey();
}
return null;
}
private void contributeToActionBars() {
IActionBars bars = getViewSite().getActionBars();
fillLocalPullDown(bars.getMenuManager());
fillLocalToolBar(bars.getToolBarManager());
}
private void fillLocalToolBar(IToolBarManager manager) {
manager.add(this.clear);
manager.add(this.scrollLock);
manager.add(this.plotterFilter);
manager.add(this.connect);
manager.add(this.disconnect);
}
private void fillLocalPullDown(IMenuManager manager) {
manager.add(this.connect);
manager.add(new Separator());
manager.add(this.disconnect);
}
private void makeActions() {
this.connect = new Action() {
@SuppressWarnings("synthetic-access")
@Override
public void run() {
OpenSerialDialogBox comportSelector = new OpenSerialDialogBox(SerialMonitor.this.parent.getShell());
comportSelector.create();
if (comportSelector.open() == Window.OK) {
connectSerial(comportSelector.GetComPort(), comportSelector.GetBaudRate());
}
}
};
this.connect.setText(Messages.serialMonitorConnectedTo);
this.connect.setToolTipText(Messages.serialMonitorAddConnectionToSeralMonitor);
this.connect.setImageDescriptor(
PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(ISharedImages.IMG_OBJ_ADD)); // IMG_OBJS_INFO_TSK));
this.disconnect = new Action() {
@Override
public void run() {
disConnectSerialPort(getSerialMonitor().serialPorts.getCombo().getText());
}
};
this.disconnect.setText(Messages.serialMonitorDisconnectedFrom);
this.disconnect.setToolTipText(Messages.serialMonitorRemoveSerialPortFromMonitor);
this.disconnect.setImageDescriptor(
PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(ISharedImages.IMG_ELCL_REMOVE));// IMG_OBJS_INFO_TSK));
this.disconnect.setEnabled(this.serialConnections.size() != 0);
this.clear = new Action(Messages.serialMonitorClear) {
@Override
public void run() {
SerialMonitor.this.monitorOutput.setText(""); //$NON-NLS-1$
}
};
this.clear.setImageDescriptor(ImageDescriptor.createFromURL(IMG_CLEAR));
this.clear.setEnabled(true);
this.scrollLock = new Action(Messages.serialMonitorScrollLock, IAction.AS_CHECK_BOX) {
@Override
public void run() {
MyPreferences.setLastUsedAutoScroll(!this.isChecked());
}
};
this.scrollLock.setImageDescriptor(ImageDescriptor.createFromURL(IMG_LOCK));
this.scrollLock.setEnabled(true);
this.scrollLock.setChecked(!MyPreferences.getLastUsedAutoScroll());
this.plotterFilter = new Action(Messages.serialMonitorFilterPloter, IAction.AS_CHECK_BOX) {
@Override
public void run() {
SerialListener.setPlotterFilter(this.isChecked());
MyPreferences.setLastUsedPlotterFilter(this.isChecked());
}
};
this.plotterFilter.setImageDescriptor(ImageDescriptor.createFromURL(IMG_FILTER));
this.plotterFilter.setEnabled(true);
this.plotterFilter.setChecked(MyPreferences.getLastUsedPlotterFilter());
SerialListener.setPlotterFilter(MyPreferences.getLastUsedPlotterFilter());
}
/**
* Passing the focus request to the viewer's control.
*/
@Override
public void setFocus() {
// MonitorOutput.setf .getControl().setFocus();
this.parent.getShell().setDefaultButton(this.send);
}
/**
* The listener calls this method to report that serial data has arrived
*
* @param stInfo
* The serial data that has arrived
* @param style
* The style that should be used to report the data; Actually
* this is the index number of the opened port
*/
public void ReportSerialActivity(String stInfo, int style) {
int startPoint = this.monitorOutput.getCharCount();
this.monitorOutput.append(stInfo);
StyleRange styleRange = new StyleRange();
styleRange.start = startPoint;
styleRange.length = stInfo.length();
styleRange.fontStyle = SWT.NORMAL;
styleRange.foreground = this.colorRegistry.get(this.serialColorID[style]);
this.monitorOutput.setStyleRange(styleRange);
if (!this.scrollLock.isChecked()) {
this.monitorOutput.setSelection(this.monitorOutput.getCharCount());
}
}
/**
* method to make sure the visualization is correct
*/
void SerialPortsUpdated() {
this.disconnect.setEnabled(this.serialConnections.size() != 0);
Serial curSelection = GetSelectedSerial();
this.serialPorts.setInput(this.serialConnections);
if (this.serialConnections.size() == 0) {
this.send.setEnabled(false);
this.reset.setEnabled(false);
} else {
if (this.serialPorts.getSelection().isEmpty()) // nothing is
// selected
{
if (curSelection == null) // nothing was selected
{
curSelection = (Serial) this.serialConnections.keySet().toArray()[0];
}
this.serialPorts.getCombo().setText(curSelection.toString());
ComboSerialChanged();
}
}
}
/**
* Connect to a serial port and sets the listener
*
* @param comPort
* the name of the com port to connect to
* @param baudRate
* the baud rate to connect to the com port
*/
public void connectSerial(String comPort, int baudRate) {
if (this.serialConnections.size() < MY_MAX_SERIAL_PORTS) {
int colorindex = this.serialConnections.size();
Serial newSerial = new Serial(comPort, baudRate);
if (newSerial.IsConnected()) {
newSerial.registerService();
SerialListener theListener = new SerialListener(this, colorindex);
newSerial.addListener(theListener);
theListener.event(System.getProperty("line.separator") + Messages.serialMonitorConnectedTo + comPort //$NON-NLS-1$
+ Messages.serialMonitorAt + baudRate + System.getProperty("line.separator")); //$NON-NLS-1$
this.serialConnections.put(newSerial, theListener);
SerialPortsUpdated();
return;
}
} else {
Activator.log(new Status(IStatus.ERROR, Activator.getId(), Messages.serialMonitorNoMoreSerialPortsSupported,
null));
}
}
public void disConnectSerialPort(String comPort) {
Serial newSerial = GetSerial(comPort);
if (newSerial != null) {
SerialListener theListener = SerialMonitor.this.serialConnections.get(newSerial);
SerialMonitor.this.serialConnections.remove(newSerial);
newSerial.removeListener(theListener);
newSerial.dispose();
theListener.dispose();
SerialPortsUpdated();
}
}
/**
*
*/
public void ComboSerialChanged() {
this.send.setEnabled(this.serialPorts.toString().length() > 0);
this.reset.setEnabled(this.serialPorts.toString().length() > 0);
this.parent.getShell().setDefaultButton(this.send);
}
/**
* PauzePort is called when the monitor needs to disconnect from a port for
* a short while. For instance when a upload is started to a com port the
* serial monitor will get a pauzeport for this com port. When the upload is
* done ResumePort will be called
*/
@Override
public boolean PauzePort(String portName) {
Serial theSerial = GetSerial(portName);
if (theSerial != null) {
theSerial.disconnect();
return true;
}
return false;
}
/**
* see PauzePort
*/
@Override
public void ResumePort(String portName) {
Serial theSerial = GetSerial(portName);
if (theSerial != null) {
if (MyPreferences.getCleanSerialMonitorAfterUpload()) {
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
SerialMonitor.this.monitorOutput.setText(""); //$NON-NLS-1$
}
});
}
theSerial.connect(15);
}
}
}