1 package net.mograsim.logic.model;
3 import java.util.ArrayList;
4 import java.util.HashMap;
7 import java.util.concurrent.atomic.AtomicBoolean;
8 import java.util.function.Consumer;
10 import org.eclipse.swt.SWT;
11 import org.eclipse.swt.graphics.Color;
12 import org.eclipse.swt.layout.GridData;
13 import org.eclipse.swt.layout.GridLayout;
14 import org.eclipse.swt.widgets.Button;
15 import org.eclipse.swt.widgets.Combo;
16 import org.eclipse.swt.widgets.Composite;
17 import org.eclipse.swt.widgets.Event;
18 import org.eclipse.swt.widgets.Label;
19 import org.eclipse.swt.widgets.Listener;
20 import org.eclipse.swt.widgets.Shell;
21 import org.eclipse.swt.widgets.Text;
23 import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
24 import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
25 import net.haspamelodica.swt.helper.zoomablecanvas.ZoomableCanvas;
26 import net.mograsim.logic.core.types.Bit;
27 import net.mograsim.logic.core.types.BitVector;
28 import net.mograsim.logic.core.types.BitVectorFormatter;
29 import net.mograsim.logic.model.model.LogicModel;
30 import net.mograsim.logic.model.model.components.ModelComponent;
31 import net.mograsim.logic.model.model.components.submodels.SubmodelComponent;
32 import net.mograsim.logic.model.snippets.highlevelstatehandlers.DefaultHighLevelStateHandler;
33 import net.mograsim.preferences.Preferences;
36 * Simulation visualizer canvas.
38 * @author Daniel Kirschten
40 public class LogicUICanvas extends ZoomableCanvas
42 private final LogicModel model;
44 public LogicUICanvas(Composite parent, int style, LogicModel model)
46 super(parent, style, Preferences.current().getBoolean("net.mograsim.logic.model.improvetext"));
50 LogicUIRenderer renderer = new LogicUIRenderer(model);
51 addZoomedRenderer(gc ->
53 Color background = Preferences.current().getColor("net.mograsim.logic.model.color.background");
54 if (background != null)
55 setBackground(background);// this.setBackground, not gc.setBackground to have the background fill the canvas
56 renderer.render(gc, new Rectangle(-offX / zoom, -offY / zoom, gW / zoom, gH / zoom));
58 model.setRedrawHandler(() ->
64 addListener(SWT.MouseDown, this::mouseDown);
66 if (Preferences.current().getBoolean("net.mograsim.logic.model.debug.openhlsshell"))
67 openDebugSetHighLevelStateShell(model);
70 private void mouseDown(Event e)
72 if (e.button == Preferences.current().getInt("net.mograsim.logic.model.button.action"))
74 Point click = canvasToWorldCoords(e.x, e.y);
75 for (ModelComponent component : model.getComponentsByName().values())
76 if (component.getBounds().contains(click) && component.clicked(click.x, click.y))
84 private void openDebugSetHighLevelStateShell(LogicModel model)
86 Shell debugShell = new Shell();
87 debugShell.setLayout(new GridLayout(2, false));
88 new Label(debugShell, SWT.NONE).setText("Target component: ");
89 Combo componentSelector = new Combo(debugShell, SWT.DROP_DOWN | SWT.READ_ONLY);
90 componentSelector.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
91 List<ModelComponent> componentsByItemIndex = new ArrayList<>();
92 List<LogicModel> models = new ArrayList<>();
93 AtomicBoolean recalculateQueued = new AtomicBoolean();
94 @SuppressWarnings("unchecked")
95 Consumer<? super ModelComponent>[] compAdded = new Consumer[1];
96 @SuppressWarnings("unchecked")
97 Consumer<? super ModelComponent>[] compRemoved = new Consumer[1];
98 compAdded[0] = c -> compsChanged(compAdded[0], compRemoved[0], c, models, componentsByItemIndex, componentSelector, model,
99 recalculateQueued, true);
100 compRemoved[0] = c -> compsChanged(compAdded[0], compRemoved[0], c, models, componentsByItemIndex, componentSelector, model,
101 recalculateQueued, false);
102 iterateModelTree(compAdded[0], compRemoved[0], model, models, true);
103 debugShell.addListener(SWT.Dispose, e -> models.forEach(m ->
105 m.removeComponentAddedListener(compAdded[0]);
106 m.removeComponentRemovedListener(compRemoved[0]);
108 queueRecalculateComponentSelector(recalculateQueued, componentsByItemIndex, componentSelector, model);
109 new Label(debugShell, SWT.NONE).setText("Target state ID: ");
110 Text stateIDText = new Text(debugShell, SWT.SINGLE | SWT.LEAD | SWT.BORDER);
111 stateIDText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
112 new Label(debugShell, SWT.NONE).setText("Value type: ");
113 Composite radioGroup = new Composite(debugShell, SWT.NONE);
114 radioGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
115 GridLayout radioGroupLayout = new GridLayout(2, false);
116 radioGroupLayout.marginHeight = 0;
117 radioGroupLayout.marginWidth = 0;
118 radioGroup.setLayout(radioGroupLayout);
119 Button radioBit = new Button(radioGroup, SWT.RADIO);
120 radioBit.setText("Single bit");
121 Button radioBitVector = new Button(radioGroup, SWT.RADIO);
122 radioBitVector.setText("Bitvector");
123 new Label(debugShell, SWT.NONE).setText("Value string representation: \n(Bit vectors: MSBit...LSBit)");
124 Text valueText = new Text(debugShell, SWT.SINGLE | SWT.LEAD | SWT.BORDER);
125 valueText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
126 Button send = new Button(debugShell, SWT.PUSH);
127 send.setText("Send!");
128 Button addListener = new Button(debugShell, SWT.PUSH);
129 addListener.setText("Add sysout listener");
130 Button get = new Button(debugShell, SWT.PUSH);
132 Button removeListener = new Button(debugShell, SWT.PUSH);
133 removeListener.setText("Remove sysout listener");
134 Text output = new Text(debugShell, SWT.READ_ONLY);
135 output.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
136 Listener sendAction = e ->
140 int componentIndex = componentSelector.getSelectionIndex();
141 if (componentIndex < 0 || componentIndex >= componentsByItemIndex.size())
142 throw new RuntimeException("No component selected");
143 ModelComponent target = componentsByItemIndex.get(componentIndex);
144 String valueString = valueText.getText();
145 String stateID = stateIDText.getText();
147 if (radioBit.getSelection())
148 value = Bit.parse(valueString);
149 else if (radioBitVector.getSelection())
151 Object hls = target.getHighLevelState(stateID);
153 if (hls instanceof Bit)
155 else if (hls instanceof BitVector)
156 width = ((BitVector) hls).length();
159 value = BitVectorFormatter.parseUserBitVector(valueString, width);
161 throw new RuntimeException("No value type selected");
162 target.setHighLevelState(stateID, value);
163 output.setText("Success!");
167 output.setText(x.getClass().getSimpleName() + (x.getMessage() == null ? "" : ": " + x.getMessage()));
170 Listener getAction = e ->
174 int componentIndex = componentSelector.getSelectionIndex();
175 if (componentIndex < 0 || componentIndex >= componentsByItemIndex.size())
176 throw new RuntimeException("No component selected");
177 output.setText("Success! Value: \r\n"
178 + componentsByItemIndex.get(componentSelector.getSelectionIndex()).getHighLevelState(stateIDText.getText()));
182 output.setText(x.getClass().getSimpleName() + (x.getMessage() == null ? "" : ": " + x.getMessage()));
185 send.addListener(SWT.Selection, sendAction);
186 valueText.addListener(SWT.DefaultSelection, sendAction);
187 get.addListener(SWT.Selection, getAction);
188 stateIDText.addListener(SWT.DefaultSelection, getAction);
189 Map<ModelComponent, Map<String, Consumer<Object>>> sysoutListenersPerHLSPerTarget = new HashMap<>();
190 addListener.addListener(SWT.Selection, e ->
194 int componentIndex = componentSelector.getSelectionIndex();
195 if (componentIndex < 0 || componentIndex >= componentsByItemIndex.size())
196 throw new RuntimeException("No component selected");
197 ModelComponent target = componentsByItemIndex.get(componentIndex);
198 Map<String, Consumer<Object>> sysoutListenersPerHLS = sysoutListenersPerHLSPerTarget.computeIfAbsent(target,
199 k -> new HashMap<>());
200 String stateIDString = stateIDText.getText();
201 if (sysoutListenersPerHLS.containsKey(stateIDString))
202 throw new RuntimeException("Listener already registered");
203 Consumer<Object> sysoutListener = v -> System.out.println(stateIDString + ": " + v);
204 target.addHighLevelStateListener(stateIDString, sysoutListener);
205 sysoutListenersPerHLS.put(stateIDString, sysoutListener);
206 output.setText("Success!");
210 output.setText(x.getClass().getSimpleName() + (x.getMessage() == null ? "" : ": " + x.getMessage()));
213 removeListener.addListener(SWT.Selection, e ->
217 int componentIndex = componentSelector.getSelectionIndex();
218 if (componentIndex < 0 || componentIndex >= componentsByItemIndex.size())
219 throw new RuntimeException("No component selected");
220 ModelComponent target = componentsByItemIndex.get(componentIndex);
221 Map<String, Consumer<Object>> sysoutListenersPerHLS = sysoutListenersPerHLSPerTarget.get(target);
222 if (sysoutListenersPerHLS == null)
223 throw new RuntimeException("Listener not registered");
224 String stateIDString = stateIDText.getText();
225 Consumer<Object> sysoutListener = sysoutListenersPerHLS.remove(stateIDString);
226 if (sysoutListener == null)
227 throw new RuntimeException("Listener not registered");
228 target.removeHighLevelStateListener(stateIDString, sysoutListener);
229 output.setText("Success!");
233 output.setText(x.getClass().getSimpleName() + (x.getMessage() == null ? "" : ": " + x.getMessage()));
237 addDisposeListener(e -> debugShell.dispose());
240 private void compsChanged(Consumer<? super ModelComponent> compAdded, Consumer<? super ModelComponent> compRemoved, ModelComponent c,
241 List<LogicModel> models, List<ModelComponent> componentsByItemIndex, Combo componentSelector, LogicModel model,
242 AtomicBoolean recalculateQueued, boolean add)
244 iterateSubmodelTree(compAdded, compRemoved, c, models, add);
245 queueRecalculateComponentSelector(recalculateQueued, componentsByItemIndex, componentSelector, model);
248 private void iterateSubmodelTree(Consumer<? super ModelComponent> compAdded, Consumer<? super ModelComponent> compRemoved,
249 ModelComponent c, List<LogicModel> models, boolean add)
251 if (c instanceof SubmodelComponent)
252 iterateModelTree(compAdded, compRemoved, ((SubmodelComponent) c).submodel, models, add);
255 private void iterateModelTree(Consumer<? super ModelComponent> compAdded, Consumer<? super ModelComponent> compRemoved,
256 LogicModel model, List<LogicModel> models, boolean add)
258 if (add ^ models.contains(model))
263 model.addComponentAddedListener(compAdded);
264 model.addComponentRemovedListener(compRemoved);
267 models.remove(model);
268 model.removeComponentAddedListener(compAdded);
269 model.removeComponentRemovedListener(compRemoved);
271 for (ModelComponent c : model.getComponentsByName().values())
272 iterateSubmodelTree(compAdded, compRemoved, c, models, add);
276 private void queueRecalculateComponentSelector(AtomicBoolean recalculateQueued, List<ModelComponent> componentsByItemIndex,
277 Combo componentSelector, LogicModel model)
279 if (recalculateQueued.compareAndSet(false, true))
280 getDisplay().asyncExec(() -> recalculateComponentSelector(recalculateQueued, componentsByItemIndex, componentSelector, model));
283 private void recalculateComponentSelector(AtomicBoolean recalculateQueued, List<ModelComponent> componentsByItemIndex,
284 Combo componentSelector, LogicModel model)
286 recalculateQueued.set(false);
287 componentsByItemIndex.clear();
288 componentSelector.setItems();
289 addComponentSelectorItems(componentsByItemIndex, "", componentSelector, model,
290 Preferences.current().getInt("net.mograsim.logic.model.debug.hlsshelldepth") - 1);
293 private void addComponentSelectorItems(List<ModelComponent> componentsByItemIndex, String base, Combo componentSelector,
294 LogicModel model, int depth)
296 model.getComponentsByName().values().stream().sorted((c1, c2) -> c1.getName().compareTo(c2.getName())).forEach(c ->
298 if (!(c.getHighLevelStateHandler() instanceof DefaultHighLevelStateHandler))
300 String item = base + c.getName();
301 componentsByItemIndex.add(c);
302 componentSelector.add(item);
303 // this causes negative numbers to result in infinite depth
304 if (depth != 0 && c instanceof SubmodelComponent)
305 addComponentSelectorItems(componentsByItemIndex, item + " -> ", componentSelector, ((SubmodelComponent) c).submodel,