1 package net.mograsim.plugin.views;
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;
7 import java.math.BigDecimal;
8 import java.math.MathContext;
9 import java.util.HashSet;
10 import java.util.Optional;
12 import java.util.function.Consumer;
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;
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;
48 public class SimulationView extends ViewPart
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);
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;
65 private MachineDebugTarget debugTarget;
66 private LogicUICanvas canvas;
68 private final MemoryCellModifiedListener memCellListener;
69 private final LogicObserver clockObserver;
70 private final MachineDebugContextListener debugContextListener;
71 private final Consumer<Double> executionSpeedListener;
73 public SimulationView()
75 controlsToDisableWhenNoMachinePresent = new HashSet<>();
76 memCellListener = a -> instPreview.refresh();
77 // TODO use Step Over instead
80 if (((CoreClock) o).isOn())
81 SafeRunner.run(() -> debugTarget.suspend());
83 debugContextListener = new MachineDebugContextListener()
86 public void machineDebugContextChanged(Optional<MachineDebugTarget> oldTarget, Optional<MachineDebugTarget> newTarget)
88 SimulationView.this.debugContextChanged(newTarget);
91 executionSpeedListener = this::speedFactorChanged;
95 public void createPartControl(Composite parent)
98 parent.setLayout(new GridLayout());
100 addSimulationControlWidgets(parent);
102 Composite contextDependentControlsParentParent = new Composite(parent, SWT.NONE);
103 contextDependentControlsParentParent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
104 contextDependentControlsParentParent.setLayout(new OverlappingFillLayout());
106 noRunningMachineLabel = new Label(contextDependentControlsParentParent, SWT.NONE);
107 noRunningMachineLabel.setText("No machine running && selected in the Debug view...");
109 contextDependentControlsParent = new Composite(contextDependentControlsParentParent, SWT.NONE);
110 GridLayout contexDependentControlsLayout = new GridLayout();
111 contexDependentControlsLayout.marginWidth = 0;
112 contexDependentControlsLayout.marginHeight = 0;
113 contextDependentControlsParent.setLayout(contexDependentControlsLayout);
115 canvasParent = new Composite(contextDependentControlsParent, SWT.NONE);
116 canvasParent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
117 canvasParent.setLayout(new FillLayout());
119 addInstructionPreviewControlWidgets(contextDependentControlsParent);
121 IDebugContextManager debugCManager = DebugUITools.getDebugContextManager();
122 IDebugContextService contextService = debugCManager.getContextService(PlatformUI.getWorkbench().getActiveWorkbenchWindow());
123 contextService.addDebugContextListener(debugContextListener);
124 debugContextListener.debugContextChanged(contextService.getActiveContext());
127 private void addSimulationControlWidgets(Composite parent)
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));
133 sbseButton = new Button(c, SWT.CHECK);
134 controlsToDisableWhenNoMachinePresent.add(sbseButton);
136 sbseButton.setText("Step by step execution");
137 sbseButton.addListener(SWT.Selection, e ->
139 CoreClock cl = debugTarget.getMachine().getClock();
140 if (sbseButton.getSelection())
141 cl.registerObserver(clockObserver);
143 cl.deregisterObserver(clockObserver);
145 sbseButton.setSelection(false);
147 Label simSpeedLabel = new Label(c, SWT.NONE);
148 controlsToDisableWhenNoMachinePresent.add(simSpeedLabel);
149 simSpeedLabel.setText("Simulation Speed: ");
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 ->
159 double speed = Math.pow(SIM_SPEED_SCALE_STEP_FACTOR, simSpeedScale.getSelection() - SIM_SPEED_SCALE_STEPS);
160 debugTarget.setExecutionSpeed(speed);
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 ->
170 debugTarget.setExecutionSpeed(speed);
172 debugTarget.setExecutionSpeed(Math.pow(10, -simSpeedInput.getPrecision()));
175 simSpeedDescription = new Label(c, SWT.NONE);
176 simSpeedDescription.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
181 private String describeSimSpeed(double speed)
183 CoreModelParameters coreModelParameters = debugTarget.getMachine().getCoreModelParameters();
184 // TODO hardcoding this seems not optimal
185 int ticksPerSecond = 1000000;
187 double simulTicksPerRealSecond = speed * ticksPerSecond;
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();
201 private static String formatNSignificantDigits(int digits, double d)
203 return new BigDecimal(d, new MathContext(digits)).toPlainString();
206 private void speedFactorChanged(double speed)
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));
214 private void addInstructionPreviewControlWidgets(Composite parent)
216 instPreview = new InstructionTable(parent, new DisplaySettings(), getSite().getWorkbenchWindow().getWorkbench().getThemeManager(),
218 instPreview.getTableViewer().getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
219 contentProvider = new ActiveInstructionPreviewContentProvider(instPreview.getTableViewer());
220 instPreview.setContentProvider(contentProvider);
223 private void debugContextChanged(Optional<MachineDebugTarget> newTarget)
225 // call binToDebugTarget even if we didn't find a selected MachineDebugTarget
226 bindToDebugTarget(newTarget.orElse(null));
229 private void bindToDebugTarget(MachineDebugTarget debugTarget)
231 deregisterMachineDependentListeners();
232 this.debugTarget = debugTarget;
234 if (canvasParent == null)
235 // createPartControls has not been called yet
243 offX = canvas.getOffX();
244 offY = canvas.getOffY();
245 zoom = canvas.getZoom();
254 if (debugTarget != null)
256 noRunningMachineLabel.setVisible(false);
257 contextDependentControlsParent.setVisible(true);
258 controlsToDisableWhenNoMachinePresent.forEach(c -> c.setEnabled(true));
260 Machine machine = debugTarget.getMachine();
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();
273 canvas.moveTo(offX, offY, zoom);
274 canvas.commitTransform();
277 AssignableMicroInstructionMemory mIMemory = machine.getMicroInstructionMemory();
278 instPreview.bindMicroInstructionMemory(mIMemory);
279 mIMemory.registerCellModifiedListener(memCellListener);
281 canvasParent.layout();
284 contentProvider.setMachine(machine);
286 // initialize executer
287 debugTarget.addExecutionSpeedListener(executionSpeedListener);
288 speedFactorChanged(debugTarget.getExecutionSpeed());
291 noRunningMachineLabel.setVisible(true);
292 contextDependentControlsParent.setVisible(false);
293 controlsToDisableWhenNoMachinePresent.forEach(c -> c.setEnabled(false));
294 contentProvider.setMachine(null);
298 private void deregisterMachineDependentListeners()
300 if (debugTarget != null)
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);
311 public void setFocus()
313 if (canvas != null && !canvas.isDisposed())
318 public void dispose()
320 deregisterMachineDependentListeners();
321 contentProvider.setMachine(null);
322 DebugUITools.getDebugContextManager().removeDebugContextListener(debugContextListener);