Fixed SimulationView spamming exceptions in some situations:
[Mograsim.git] / plugins / net.mograsim.plugin.core / src / net / mograsim / plugin / views / SimulationView.java
1 package net.mograsim.plugin.views;
2
3 import java.util.HashSet;
4 import java.util.Set;
5 import java.util.function.Consumer;
6
7 import org.eclipse.core.runtime.SafeRunner;
8 import org.eclipse.debug.core.ILaunch;
9 import org.eclipse.debug.core.model.IDebugTarget;
10 import org.eclipse.debug.ui.DebugUITools;
11 import org.eclipse.debug.ui.contexts.IDebugContextListener;
12 import org.eclipse.debug.ui.contexts.IDebugContextManager;
13 import org.eclipse.debug.ui.contexts.IDebugContextService;
14 import org.eclipse.jface.viewers.ISelection;
15 import org.eclipse.jface.viewers.TreeSelection;
16 import org.eclipse.swt.SWT;
17 import org.eclipse.swt.layout.FillLayout;
18 import org.eclipse.swt.layout.GridData;
19 import org.eclipse.swt.layout.GridLayout;
20 import org.eclipse.swt.widgets.Button;
21 import org.eclipse.swt.widgets.Composite;
22 import org.eclipse.swt.widgets.Control;
23 import org.eclipse.swt.widgets.Label;
24 import org.eclipse.swt.widgets.Scale;
25 import org.eclipse.ui.PlatformUI;
26 import org.eclipse.ui.part.ViewPart;
27
28 import net.haspamelodica.swt.helper.input.DoubleInput;
29 import net.haspamelodica.swt.helper.zoomablecanvas.helper.ZoomableCanvasUserInput;
30 import net.mograsim.logic.core.LogicObserver;
31 import net.mograsim.logic.core.components.CoreClock;
32 import net.mograsim.logic.model.LogicUICanvas;
33 import net.mograsim.machine.Machine;
34 import net.mograsim.machine.Memory.MemoryCellModifiedListener;
35 import net.mograsim.machine.mi.AssignableMicroInstructionMemory;
36 import net.mograsim.plugin.launch.MachineDebugTarget;
37 import net.mograsim.plugin.tables.DisplaySettings;
38 import net.mograsim.plugin.tables.mi.ActiveInstructionPreviewContentProvider;
39 import net.mograsim.plugin.tables.mi.InstructionTable;
40 import net.mograsim.plugin.util.OverlappingFillLayout;
41 import net.mograsim.preferences.Preferences;
42
43 public class SimulationView extends ViewPart
44 {
45         private static final int SIM_SPEED_SCALE_STEPS = 50;
46         private static final double SIM_SPEED_SCALE_STEP_FACTOR = 1.32;
47         private static final double SIM_SPEED_SCALE_STEP_FACTOR_LOG = Math.log(SIM_SPEED_SCALE_STEP_FACTOR);
48
49         private final Set<Control> controlsToDisableWhenNoMachinePresent;
50         private Scale simSpeedScale;
51         private DoubleInput simSpeedInput;
52         private Composite contextDependentControlsParent;
53         private Composite canvasParent;
54         private InstructionTable instPreview;
55         private ActiveInstructionPreviewContentProvider contentProvider;
56         private Label noRunningMachineLabel;
57
58         private MachineDebugTarget debugTarget;
59         private LogicUICanvas canvas;
60
61         private final MemoryCellModifiedListener memCellListener;
62         private final LogicObserver clockObserver;
63         private final IDebugContextListener debugContextListener;
64         private final Consumer<Double> executionSpeedListener;
65
66         public SimulationView()
67         {
68                 controlsToDisableWhenNoMachinePresent = new HashSet<>();
69                 memCellListener = a -> instPreview.refresh();
70                 // TODO could this be a breakpoint?
71                 clockObserver = o ->
72                 {
73                         if (((CoreClock) o).isOn())
74                                 SafeRunner.run(() -> debugTarget.suspend());
75                 };
76                 debugContextListener = e -> debugContextChanged(e.getContext());
77                 executionSpeedListener = this::speedFactorChanged;
78         }
79
80         @Override
81         public void createPartControl(Composite parent)
82         {
83                 // initialize UI
84                 parent.setLayout(new GridLayout());
85
86                 addSimulationControlWidgets(parent);
87
88                 Composite contextDependentControlsParentParent = new Composite(parent, SWT.NONE);
89                 contextDependentControlsParentParent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
90                 contextDependentControlsParentParent.setLayout(new OverlappingFillLayout());
91
92                 noRunningMachineLabel = new Label(contextDependentControlsParentParent, SWT.NONE);
93                 noRunningMachineLabel.setText("No machine running && selected in the Debug view...");
94
95                 contextDependentControlsParent = new Composite(contextDependentControlsParentParent, SWT.NONE);
96                 GridLayout contexDependentControlsLayout = new GridLayout();
97                 contexDependentControlsLayout.marginWidth = 0;
98                 contexDependentControlsLayout.marginHeight = 0;
99                 contextDependentControlsParent.setLayout(contexDependentControlsLayout);
100
101                 canvasParent = new Composite(contextDependentControlsParent, SWT.NONE);
102                 canvasParent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
103                 canvasParent.setLayout(new FillLayout());
104
105                 addInstructionPreviewControlWidgets(contextDependentControlsParent);
106
107                 IDebugContextManager debugCManager = DebugUITools.getDebugContextManager();
108                 IDebugContextService contextService = debugCManager.getContextService(PlatformUI.getWorkbench().getActiveWorkbenchWindow());
109                 contextService.addDebugContextListener(debugContextListener);
110                 debugContextChanged(contextService.getActiveContext());
111         }
112
113         private void addSimulationControlWidgets(Composite parent)
114         {
115                 Composite c = new Composite(parent, SWT.NONE);
116                 c.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
117                 c.setLayout(new GridLayout(7, false));
118
119                 Button sbseButton = new Button(c, SWT.CHECK);
120                 controlsToDisableWhenNoMachinePresent.add(sbseButton);
121
122                 sbseButton.setText("Step by step execution");
123                 sbseButton.addListener(SWT.Selection, e ->
124                 {
125                         CoreClock cl = debugTarget.getMachine().getClock();
126                         if (sbseButton.getSelection())
127                                 cl.registerObserver(clockObserver);
128                         else
129                                 cl.deregisterObserver(clockObserver);
130                 });
131                 sbseButton.setSelection(false);
132
133                 Label simSpeedLabel = new Label(c, SWT.NONE);
134                 controlsToDisableWhenNoMachinePresent.add(simSpeedLabel);
135                 simSpeedLabel.setText("Simulation Speed: ");
136
137                 simSpeedScale = new Scale(c, SWT.NONE);
138                 controlsToDisableWhenNoMachinePresent.add(simSpeedScale);
139                 simSpeedScale.setMinimum(0);
140                 simSpeedScale.setMaximum(SIM_SPEED_SCALE_STEPS);
141                 simSpeedScale.setIncrement(1);
142                 simSpeedScale.setSelection(0);
143                 simSpeedScale.addListener(SWT.Selection, e ->
144                 {
145                         double speed = Math.pow(SIM_SPEED_SCALE_STEP_FACTOR, simSpeedScale.getSelection() - SIM_SPEED_SCALE_STEPS);
146                         debugTarget.setExecutionSpeed(speed);
147                 });
148
149                 simSpeedInput = new DoubleInput(c, SWT.NONE);
150                 controlsToDisableWhenNoMachinePresent.add(simSpeedInput);
151                 simSpeedInput.setPrecision(Preferences.current().getInt("net.mograsim.plugin.core.simspeedprecision"));
152                 simSpeedInput.addChangeListener(speed ->
153                 {
154                         if (speed != 0)
155                                 debugTarget.setExecutionSpeed(speed);
156                         else
157                                 debugTarget.setExecutionSpeed(Math.pow(10, -simSpeedInput.getPrecision()));
158                 });
159
160                 c.layout();
161         }
162
163         private void speedFactorChanged(double speed)
164         {
165                 simSpeedInput.setValue(speed);
166                 int closestScalePos = (int) Math.round(Math.log(speed) / SIM_SPEED_SCALE_STEP_FACTOR_LOG + SIM_SPEED_SCALE_STEPS);
167                 simSpeedScale.setSelection(Math.min(Math.max(closestScalePos, 0), SIM_SPEED_SCALE_STEPS));
168         }
169
170         private void addInstructionPreviewControlWidgets(Composite parent)
171         {
172                 instPreview = new InstructionTable(parent, new DisplaySettings(), getSite().getWorkbenchWindow().getWorkbench().getThemeManager());
173                 instPreview.getTableViewer().getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
174                 contentProvider = new ActiveInstructionPreviewContentProvider(instPreview.getTableViewer());
175                 instPreview.setContentProvider(contentProvider);
176         }
177
178         private void debugContextChanged(ISelection selection)
179         {
180                 if (selection != null && selection instanceof TreeSelection)
181                 {
182                         TreeSelection treeSelection = (TreeSelection) selection;
183                         Object[] selectedElements = treeSelection.toArray();
184                         for (Object selectedElement : selectedElements)
185                         {
186                                 MachineDebugTarget debugTarget;
187                                 if (selectedElement instanceof MachineDebugTarget)
188                                         debugTarget = (MachineDebugTarget) selectedElement;
189                                 else if (selectedElement instanceof ILaunch)
190                                 {
191                                         ILaunch launch = (ILaunch) selectedElement;
192                                         IDebugTarget genericDebugTarget = launch.getDebugTarget();
193                                         if (genericDebugTarget instanceof MachineDebugTarget)
194                                                 debugTarget = (MachineDebugTarget) genericDebugTarget;
195                                         else
196                                                 continue;
197                                 } else
198                                         continue;
199                                 if (debugTarget.isTerminated())
200                                         continue;
201                                 // we found a selected MachineDebugTarget
202                                 if (this.debugTarget != debugTarget)
203                                         bindToDebugTarget(debugTarget);
204                                 return;
205                         }
206                 }
207                 // we didn't find a selected MachineDebugTarget
208                 // call binToDebugTarget even if this.debugTarget==null
209                 bindToDebugTarget(null);
210         }
211
212         private void bindToDebugTarget(MachineDebugTarget debugTarget)
213         {
214                 this.debugTarget = debugTarget;
215
216                 if (canvasParent == null)
217                         // createPartControls has not been called yet
218                         return;
219
220                 double offX;
221                 double offY;
222                 double zoom;
223                 deregisterMachineDependentListeners();
224                 if (canvas != null)
225                 {
226                         offX = canvas.getOffX();
227                         offY = canvas.getOffY();
228                         zoom = canvas.getZoom();
229                         canvas.dispose();
230                 } else
231                 {
232                         offX = 0;
233                         offY = 0;
234                         zoom = -1;
235                 }
236
237                 if (debugTarget != null)
238                 {
239                         noRunningMachineLabel.setVisible(false);
240                         contextDependentControlsParent.setVisible(true);
241                         controlsToDisableWhenNoMachinePresent.forEach(c -> c.setEnabled(true));
242
243                         Machine machine = debugTarget.getMachine();
244
245                         canvas = new LogicUICanvas(canvasParent, SWT.NONE, machine.getModel());
246                         canvas.addListener(SWT.MouseDown, e -> canvas.setFocus());
247                         ZoomableCanvasUserInput userInput = new ZoomableCanvasUserInput(canvas);
248                         userInput.buttonDrag = Preferences.current().getInt("net.mograsim.logic.model.button.drag");
249                         userInput.buttonZoom = Preferences.current().getInt("net.mograsim.logic.model.button.zoom");
250                         userInput.enableUserInput();
251                         if (zoom > 0)
252                         {
253                                 canvas.moveTo(offX, offY, zoom);
254                                 canvas.commitTransform();
255                         }
256
257                         AssignableMicroInstructionMemory mIMemory = machine.getMicroInstructionMemory();
258                         instPreview.bindMicroInstructionMemory(mIMemory);
259                         mIMemory.registerCellModifiedListener(memCellListener);
260
261                         canvasParent.layout();
262
263                         // update preview
264                         contentProvider.setMachine(machine);
265
266                         // initialize executer
267                         debugTarget.addExecutionSpeedListener(executionSpeedListener);
268                         speedFactorChanged(debugTarget.getExecutionSpeed());
269                 } else
270                 {
271                         noRunningMachineLabel.setVisible(true);
272                         contextDependentControlsParent.setVisible(false);
273                         controlsToDisableWhenNoMachinePresent.forEach(c -> c.setEnabled(false));
274                         contentProvider.setMachine(null);
275                 }
276         }
277
278         private void deregisterMachineDependentListeners()
279         {
280                 if (debugTarget != null)
281                 {
282                         debugTarget.removeExecutionSpeedListener(executionSpeedListener);
283                         debugTarget.getMachine().getMicroInstructionMemory().deregisterCellModifiedListener(memCellListener);
284                         debugTarget.getMachine().getClock().deregisterObserver(clockObserver);
285                 }
286         }
287
288         @Override
289         public void setFocus()
290         {
291                 if (canvas != null && !canvas.isDisposed())
292                         canvas.setFocus();
293         }
294
295         @Override
296         public void dispose()
297         {
298                 deregisterMachineDependentListeners();
299                 contentProvider.setMachine(null);
300                 DebugUITools.getDebugContextManager().removeDebugContextListener(debugContextListener);
301                 super.dispose();
302         }
303 }