Introduced sim speed description. Fixes #16
[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                 clockObserver = o ->
79                 {
80                         if (((CoreClock) o).isOn())
81                                 SafeRunner.run(() -> debugTarget.suspend());
82                 };
83                 debugContextListener = new MachineDebugContextListener()
84                 {
85                         @Override
86                         public void machineDebugContextChanged(Optional<MachineDebugTarget> oldTarget, Optional<MachineDebugTarget> newTarget)
87                         {
88                                 SimulationView.this.debugContextChanged(newTarget);
89                         }
90                 };
91                 executionSpeedListener = this::speedFactorChanged;
92         }
93
94         @Override
95         public void createPartControl(Composite parent)
96         {
97                 // initialize UI
98                 parent.setLayout(new GridLayout());
99
100                 addSimulationControlWidgets(parent);
101
102                 Composite contextDependentControlsParentParent = new Composite(parent, SWT.NONE);
103                 contextDependentControlsParentParent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
104                 contextDependentControlsParentParent.setLayout(new OverlappingFillLayout());
105
106                 noRunningMachineLabel = new Label(contextDependentControlsParentParent, SWT.NONE);
107                 noRunningMachineLabel.setText("No machine running && selected in the Debug view...");
108
109                 contextDependentControlsParent = new Composite(contextDependentControlsParentParent, SWT.NONE);
110                 GridLayout contexDependentControlsLayout = new GridLayout();
111                 contexDependentControlsLayout.marginWidth = 0;
112                 contexDependentControlsLayout.marginHeight = 0;
113                 contextDependentControlsParent.setLayout(contexDependentControlsLayout);
114
115                 canvasParent = new Composite(contextDependentControlsParent, SWT.NONE);
116                 canvasParent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
117                 canvasParent.setLayout(new FillLayout());
118
119                 addInstructionPreviewControlWidgets(contextDependentControlsParent);
120
121                 IDebugContextManager debugCManager = DebugUITools.getDebugContextManager();
122                 IDebugContextService contextService = debugCManager.getContextService(PlatformUI.getWorkbench().getActiveWorkbenchWindow());
123                 contextService.addDebugContextListener(debugContextListener);
124                 debugContextListener.debugContextChanged(contextService.getActiveContext());
125         }
126
127         private void addSimulationControlWidgets(Composite parent)
128         {
129                 Composite c = new Composite(parent, SWT.NONE);
130                 c.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
131                 c.setLayout(new GridLayout(8, false));
132
133                 sbseButton = new Button(c, SWT.CHECK);
134                 controlsToDisableWhenNoMachinePresent.add(sbseButton);
135
136                 sbseButton.setText("Step by step execution");
137                 sbseButton.addListener(SWT.Selection, e ->
138                 {
139                         CoreClock cl = debugTarget.getMachine().getClock();
140                         if (sbseButton.getSelection())
141                                 cl.registerObserver(clockObserver);
142                         else
143                                 cl.deregisterObserver(clockObserver);
144                 });
145                 sbseButton.setSelection(false);
146
147                 Label simSpeedLabel = new Label(c, SWT.NONE);
148                 controlsToDisableWhenNoMachinePresent.add(simSpeedLabel);
149                 simSpeedLabel.setText("Simulation Speed: ");
150
151                 simSpeedScale = new Scale(c, SWT.NONE);
152                 controlsToDisableWhenNoMachinePresent.add(simSpeedScale);
153                 simSpeedScale.setMinimum(0);
154                 simSpeedScale.setMaximum(SIM_SPEED_SCALE_STEPS);
155                 simSpeedScale.setIncrement(1);
156                 simSpeedScale.setSelection(0);
157                 simSpeedScale.addListener(SWT.Selection, e ->
158                 {
159                         double speed = Math.pow(SIM_SPEED_SCALE_STEP_FACTOR, simSpeedScale.getSelection() - SIM_SPEED_SCALE_STEPS);
160                         debugTarget.setExecutionSpeed(speed);
161                 });
162
163                 simSpeedInput = new DoubleInput(c, SWT.NONE);
164                 controlsToDisableWhenNoMachinePresent.add(simSpeedInput);
165                 // TODO add a listener
166                 simSpeedInput.setPrecision(MograsimActivator.instance().getPluginPrefs().getInt(SIMULATION_SPEED_PRECISION));
167                 simSpeedInput.addChangeListener(speed ->
168                 {
169                         if (speed != 0)
170                                 debugTarget.setExecutionSpeed(speed);
171                         else
172                                 debugTarget.setExecutionSpeed(Math.pow(10, -simSpeedInput.getPrecision()));
173                 });
174
175                 simSpeedDescription = new Label(c, SWT.NONE);
176                 simSpeedDescription.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
177
178                 c.layout();
179         }
180
181         private String describeSimSpeed(double speed)
182         {
183                 CoreModelParameters coreModelParameters = debugTarget.getMachine().getCoreModelParameters();
184                 // TODO hardcoding this seems not optimal
185                 int ticksPerSecond = 1000000;
186
187                 double simulTicksPerRealSecond = speed * ticksPerSecond;
188
189                 // TODO internationalize
190                 StringBuilder sb = new StringBuilder();
191                 sb.append("Per second: ");
192                 sb.append(formatNSignificantDigits(4, simulTicksPerRealSecond / coreModelParameters.wireTravelTime));
193                 sb.append(" wire travel times; ");
194                 sb.append(formatNSignificantDigits(4, simulTicksPerRealSecond / coreModelParameters.gateProcessTime));
195                 sb.append(" gate process times; ");
196                 sb.append(formatNSignificantDigits(4, simulTicksPerRealSecond / debugTarget.getMachine().getClock().getDelta() / 2));
197                 sb.append(" clock cycles");
198                 return sb.toString();
199         }
200
201         private static String formatNSignificantDigits(int digits, double d)
202         {
203                 return new BigDecimal(d, new MathContext(digits)).toPlainString();
204         }
205
206         private void speedFactorChanged(double speed)
207         {
208                 simSpeedInput.setValue(speed);
209                 int closestScalePos = (int) Math.round(Math.log(speed) / SIM_SPEED_SCALE_STEP_FACTOR_LOG + SIM_SPEED_SCALE_STEPS);
210                 simSpeedScale.setSelection(Math.min(Math.max(closestScalePos, 0), SIM_SPEED_SCALE_STEPS));
211                 simSpeedDescription.setText(describeSimSpeed(speed));
212         }
213
214         private void addInstructionPreviewControlWidgets(Composite parent)
215         {
216                 instPreview = new InstructionTable(parent, new DisplaySettings(), getSite().getWorkbenchWindow().getWorkbench().getThemeManager(),
217                                 false);
218                 instPreview.getTableViewer().getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
219                 contentProvider = new ActiveInstructionPreviewContentProvider(instPreview.getTableViewer());
220                 instPreview.setContentProvider(contentProvider);
221         }
222
223         private void debugContextChanged(Optional<MachineDebugTarget> newTarget)
224         {
225                 // call binToDebugTarget even if we didn't find a selected MachineDebugTarget
226                 bindToDebugTarget(newTarget.orElse(null));
227         }
228
229         private void bindToDebugTarget(MachineDebugTarget debugTarget)
230         {
231                 deregisterMachineDependentListeners();
232                 this.debugTarget = debugTarget;
233
234                 if (canvasParent == null)
235                         // createPartControls has not been called yet
236                         return;
237
238                 double offX;
239                 double offY;
240                 double zoom;
241                 if (canvas != null)
242                 {
243                         offX = canvas.getOffX();
244                         offY = canvas.getOffY();
245                         zoom = canvas.getZoom();
246                         canvas.dispose();
247                 } else
248                 {
249                         offX = 0;
250                         offY = 0;
251                         zoom = -1;
252                 }
253
254                 if (debugTarget != null)
255                 {
256                         noRunningMachineLabel.setVisible(false);
257                         contextDependentControlsParent.setVisible(true);
258                         controlsToDisableWhenNoMachinePresent.forEach(c -> c.setEnabled(true));
259
260                         Machine machine = debugTarget.getMachine();
261
262                         RenderPreferences renderPrefs = MograsimActivator.instance().getRenderPrefs();
263                         canvas = new LogicUICanvas(canvasParent, SWT.NONE, machine.getModel(), renderPrefs);
264                         canvas.addListener(SWT.MouseDown, e -> canvas.setFocus());
265                         ZoomableCanvasUserInput userInput = new ZoomableCanvasUserInput(canvas);
266                         // TODO add a listener
267                         userInput.buttonDrag = renderPrefs.getInt(DRAG_BUTTON);
268                         // TODO add a listener
269                         userInput.buttonZoom = renderPrefs.getInt(ZOOM_BUTTON);
270                         userInput.enableUserInput();
271                         if (zoom > 0)
272                         {
273                                 canvas.moveTo(offX, offY, zoom);
274                                 canvas.commitTransform();
275                         }
276
277                         AssignableMicroInstructionMemory mIMemory = machine.getMicroInstructionMemory();
278                         instPreview.bindMicroInstructionMemory(mIMemory);
279                         mIMemory.registerCellModifiedListener(memCellListener);
280
281                         canvasParent.layout();
282
283                         // update preview
284                         contentProvider.setMachine(machine);
285
286                         // initialize executer
287                         debugTarget.addExecutionSpeedListener(executionSpeedListener);
288                         speedFactorChanged(debugTarget.getExecutionSpeed());
289                 } else
290                 {
291                         noRunningMachineLabel.setVisible(true);
292                         contextDependentControlsParent.setVisible(false);
293                         controlsToDisableWhenNoMachinePresent.forEach(c -> c.setEnabled(false));
294                         contentProvider.setMachine(null);
295                 }
296         }
297
298         private void deregisterMachineDependentListeners()
299         {
300                 if (debugTarget != null)
301                 {
302                         debugTarget.removeExecutionSpeedListener(executionSpeedListener);
303                         debugTarget.getMachine().getMicroInstructionMemory().deregisterCellModifiedListener(memCellListener);
304                         debugTarget.getMachine().getClock().deregisterObserver(clockObserver);
305                         if (sbseButton != null && !sbseButton.isDisposed())
306                                 sbseButton.setSelection(false);
307                 }
308         }
309
310         @Override
311         public void setFocus()
312         {
313                 if (canvas != null && !canvas.isDisposed())
314                         canvas.setFocus();
315         }
316
317         @Override
318         public void dispose()
319         {
320                 deregisterMachineDependentListeners();
321                 contentProvider.setMachine(null);
322                 DebugUITools.getDebugContextManager().removeDebugContextListener(debugContextListener);
323                 super.dispose();
324         }
325 }