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