<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
- <name>LogicUI</name>
+ <name>mograsim.logic.ui</name>
<comment></comment>
<projects>
- <project>era.mi</project>
+ <project>mograsim.logic.core</project>
<project>SWTZoomableCanvas</project>
</projects>
<buildSpec>
eclipse.preferences.version=1
editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
-formatter_profile=_ERA-MI
+formatter_profile=_MoGraSim
formatter_settings_version=16
sp_cleanup.add_default_serial_version_id=true
sp_cleanup.add_generated_serial_version_id=false
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
-Bundle-Name: LogicUI
-Bundle-SymbolicName: LogicUI;singleton:=true
+Bundle-Name: mograsim.logic.ui
+Bundle-SymbolicName: mograsim.logic.ui;singleton:=true
Bundle-Version: 1.0.0
-Export-Package: era.mi.gui,
- era.mi.gui.examples,
- era.mi.gui.model,
- era.mi.gui.model.components,
- era.mi.gui.model.wires,
- era.mi.gui.modeladapter,
- era.mi.gui.modeladapter.componentadapters
+Export-Package: mograsim.logic.ui,
+ mograsim.logic.ui.examples,
+ mograsim.logic.ui.model,
+ mograsim.logic.ui.model.components,
+ mograsim.logic.ui.model.wires,
+ mograsim.logic.ui.modeladapter,
+ mograsim.logic.ui.modeladapter.componentadapters
Bundle-RequiredExecutionEnvironment: JavaSE-10
Require-Bundle: org.eclipse.swt;bundle-version="3.110.0",
SWTZoomableCanvas;bundle-version="1.0.0";visibility:=reexport,
- era.mi;bundle-version="1.0.0";visibility:=reexport
-Automatic-Module-Name: LogicUI
+ mograsim.logic.core;bundle-version="1.0.0";visibility:=reexport
+Automatic-Module-Name: mograsim.logic.ui
+++ /dev/null
-package era.mi.gui;
-
-import java.util.function.Consumer;
-import java.util.function.Supplier;
-
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.graphics.Color;
-import org.eclipse.swt.graphics.Device;
-
-import era.mi.logic.types.ColorDefinition;
-import era.mi.logic.types.ColorDefinition.BuiltInColor;
-import net.haspamelodica.swt.helper.gcs.GeneralGC;
-
-//TODO replace with a proper ColorManager
-public class ColorHelper
-{
- public static void executeWithDifferentForeground(GeneralGC gc, ColorDefinition col, Runnable exec)
- {
- executeWithDifferentColor(gc.getDevice(), col, gc::getForeground, gc::setForeground, exec);
- }
-
- public static void executeWithDifferentBackground(GeneralGC gc, ColorDefinition col, Runnable exec)
- {
- executeWithDifferentColor(gc.getDevice(), col, gc::getBackground, gc::setBackground, exec);
- }
-
- private static void executeWithDifferentColor(Device device, ColorDefinition col, Supplier<Color> getColor, Consumer<Color> setColor,
- Runnable exec)
- {
- Color oldColor = getColor.get();
- boolean isNoSystemColor = col.builtInColor == null;
- Color newColor;
- if (isNoSystemColor)
- newColor = new Color(device, col.r, col.g, col.b);
- else
- newColor = device.getSystemColor(ColorHelper.toSWTColorConstant(col.builtInColor));
- setColor.accept(newColor);
-
- exec.run();
-
- setColor.accept(oldColor);
- if (isNoSystemColor)
- newColor.dispose();
- }
-
- public static int toSWTColorConstant(BuiltInColor col)
- {
- switch (col)
- {
- case COLOR_BLACK:
- return SWT.COLOR_BLACK;
- case COLOR_BLUE:
- return SWT.COLOR_BLUE;
- case COLOR_CYAN:
- return SWT.COLOR_CYAN;
- case COLOR_DARK_BLUE:
- return SWT.COLOR_DARK_BLUE;
- case COLOR_DARK_CYAN:
- return SWT.COLOR_DARK_CYAN;
- case COLOR_DARK_GRAY:
- return SWT.COLOR_DARK_GRAY;
- case COLOR_DARK_GREEN:
- return SWT.COLOR_DARK_GREEN;
- case COLOR_DARK_MAGENTA:
- return SWT.COLOR_DARK_MAGENTA;
- case COLOR_DARK_RED:
- return SWT.COLOR_DARK_RED;
- case COLOR_DARK_YELLOW:
- return SWT.COLOR_DARK_YELLOW;
- case COLOR_GRAY:
- return SWT.COLOR_GRAY;
- case COLOR_GREEN:
- return SWT.COLOR_GREEN;
- case COLOR_MAGENTA:
- return SWT.COLOR_MAGENTA;
- case COLOR_RED:
- return SWT.COLOR_RED;
- case COLOR_WHITE:
- return SWT.COLOR_WHITE;
- case COLOR_YELLOW:
- return SWT.COLOR_YELLOW;
- default:
- throw new IllegalArgumentException("Unknown enum constant: " + col);
- }
- }
-
- private ColorHelper()
- {
- throw new UnsupportedOperationException("No instances of ColorHelper");
- }
-}
\ No newline at end of file
+++ /dev/null
-package era.mi.gui;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicLong;
-
-import era.mi.logic.timeline.Timeline;
-
-//TODO maybe move to logic core?
-public class LogicExecuter
-{
- // TODO replace with LogicModel when it exists
- private final Timeline timeline;
-
- private final AtomicBoolean shouldBeRunningLive;
- private final AtomicBoolean isRunningLive;
- private final AtomicLong nextExecSimulTime;
- private final Thread simulationThread;
-
- public LogicExecuter(Timeline timeline)
- {
- this.timeline = timeline;
-
- timeline.setTimeFunction(System::currentTimeMillis);
- shouldBeRunningLive = new AtomicBoolean();
- isRunningLive = new AtomicBoolean();
- nextExecSimulTime = new AtomicLong();
- simulationThread = new Thread(() ->
- {
- isRunningLive.set(true);
- synchronized (isRunningLive)
- {
- isRunningLive.notify();
- }
- while (shouldBeRunningLive.get())
- {
- // always execute to keep timeline from "hanging behind" for too long
- long current = System.currentTimeMillis();
- timeline.executeUntil(timeline.laterThan(current), current + 10);
- long sleepTime;
- if (timeline.hasNext())
- sleepTime = timeline.nextEventTime() - current;
- else
- sleepTime = 10000;
- try
- {
- nextExecSimulTime.set(current + sleepTime);
- if (sleepTime > 0)
- Thread.sleep(sleepTime);
- }
- catch (@SuppressWarnings("unused") InterruptedException e)
- {// do nothing; it is normal execution flow to be interrupted
- }
- }
- isRunningLive.set(false);
- synchronized (isRunningLive)
- {
- isRunningLive.notify();
- }
- });
- timeline.addEventAddedListener(event ->
- {
- if (isRunningLive.get())
- if (Timeline.timeCmp(event.getTiming(), nextExecSimulTime.get()) < 0)
- simulationThread.interrupt();
- });
- }
-
- public void executeNextStep()
- {
- timeline.executeNext();
- }
-
- public synchronized void startLiveExecution()
- {
- if (shouldBeRunningLive.get())
- return;
- shouldBeRunningLive.set(true);
- simulationThread.start();
- waitForIsRunning(true);
- }
-
- public synchronized void stopLiveExecution()
- {
- if (!shouldBeRunningLive.get())
- return;
- shouldBeRunningLive.set(false);
- simulationThread.interrupt();
- waitForIsRunning(false);
- }
-
- private void waitForIsRunning(boolean expectedState)
- {
- while (isRunningLive.get() ^ expectedState)
- try
- {
- synchronized (isRunningLive)
- {
- isRunningLive.wait();
- }
- }
- catch (@SuppressWarnings("unused") InterruptedException e)
- {// no need to do anything
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-package era.mi.gui;
-
-import java.util.function.Consumer;
-
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Event;
-
-import era.mi.gui.model.ViewModel;
-import era.mi.gui.model.components.GUIComponent;
-import era.mi.gui.model.wires.GUIWire;
-import era.mi.gui.model.wires.Pin;
-import net.haspamelodica.swt.helper.gcs.GeneralGC;
-import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
-import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
-import net.haspamelodica.swt.helper.zoomablecanvas.ZoomableCanvas;
-
-/**
- * Simulation visualizer canvas.
- *
- * @author Daniel Kirschten
- */
-public class LogicUICanvas extends ZoomableCanvas
-{
- private static final boolean DRAW_PINS = false;
-
- private final ViewModel model;
-
- public LogicUICanvas(Composite parent, int style, ViewModel model)
- {
- super(parent, style);
-
- this.model = model;
-
- Consumer<Object> redrawConsumer = o -> redrawThreadsafe();
- Consumer<Pin> pinAddedListener = p ->
- {
- p.addPinMovedListener(redrawConsumer);
- redrawThreadsafe();
- };
- Consumer<Pin> pinRemovedListener = p ->
- {
- p.removePinMovedListener(redrawConsumer);
- redrawThreadsafe();
- };
- Consumer<? super GUIComponent> componentAddedListener = c ->
- {
- c.addComponentLookChangedListener(redrawConsumer);
- c.addComponentMovedListener(redrawConsumer);
- c.addPinAddedListener(pinAddedListener);
- c.addPinRemovedListener(pinRemovedListener);
- redrawThreadsafe();
- };
- model.addComponentAddedListener(componentAddedListener);
- model.getComponents().forEach(componentAddedListener);
- model.addComponentRemovedListener(c ->
- {
- c.removeComponentLookChangedListener(redrawConsumer);
- c.removeComponentMovedListener(redrawConsumer);
- c.removePinAddedListener(pinAddedListener);
- c.removePinRemovedListener(pinRemovedListener);
- redrawThreadsafe();
- });
- Consumer<? super GUIWire> wireAddedListener = w ->
- {
- w.addWireLookChangedListener(redrawConsumer);
- redrawThreadsafe();
- };
- model.addWireAddedListener(wireAddedListener);
- model.getWires().forEach(wireAddedListener);
- model.addWireRemovedListener(w ->
- {
- w.removeWireLookChangedListener(redrawConsumer);
- redrawThreadsafe();
- });
-
- addZoomedRenderer(gc ->
- {
- gc.setLineWidth(.5);
- model.getWires().forEach(w -> w.render(gc));
- Rectangle visibleRegion = new Rectangle(offX, offY, gW / zoom, gH / zoom);
- model.getComponents().forEach(c -> drawComponent(gc, c, visibleRegion));
- });
- addListener(SWT.MouseDown, this::mouseDown);
- }
-
- private void drawComponent(GeneralGC gc, GUIComponent component, Rectangle visibleRegion)
- {
- component.render(gc, visibleRegion);
- if (DRAW_PINS)
- {
- gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_DARK_CYAN));
- for (Pin p : component.getPins())
- {
- Point pos = p.getPos();
- gc.fillOval(pos.x - 1, pos.y - 1, 2, 2);
- }
- }
- }
-
- private void mouseDown(Event e)
- {
- if (e.button == 1)
- {
- Point click = displayToWorldCoords(e.x, e.y);
- for (GUIComponent component : model.getComponents())
- if (component.getBounds().contains(click) && component.clicked(click.x, click.y))
- {
- redraw();
- break;
- }
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-package era.mi.gui;
-
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.FillLayout;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Shell;
-
-import era.mi.gui.model.ViewModel;
-import era.mi.gui.modeladapter.LogicModelParameters;
-import net.haspamelodica.swt.helper.zoomablecanvas.helper.ZoomableCanvasOverlay;
-import net.haspamelodica.swt.helper.zoomablecanvas.helper.ZoomableCanvasUserInput;
-
-/**
- * Standalone simulation visualizer graphical user interface.
- *
- * @author Daniel Kirschten
- */
-public class LogicUIStandaloneGUI
-{
- private final Display display;
- private final Shell shell;
- private final LogicUICanvas ui;
-
- public LogicUIStandaloneGUI(ViewModel model)
- {
- display = new Display();
- shell = new Shell(display);
- shell.setLayout(new FillLayout());
- ui = new LogicUICanvas(shell, SWT.NONE, model);
-
- ZoomableCanvasUserInput userInput = new ZoomableCanvasUserInput(ui);
- userInput.buttonDrag = 3;
- userInput.buttonZoom = 2;
- userInput.enableUserInput();
- new ZoomableCanvasOverlay(ui, null).enableScale();
-
- // TODO don't do this here
- LogicModelParameters params = new LogicModelParameters();
- params.gateProcessTime = 50;
- params.wireTravelTime = 10;
-// timeline = ViewLogicModelAdapter.convert(model, params);
- }
-
- public LogicUICanvas getLogicUICanvas()
- {
- return ui;
- }
-
- /**
- * Opens the UI shell. Returns when the shell is closed.
- */
- public void run()
- {
- shell.open();
- while (!shell.isDisposed())
- if (!display.readAndDispatch())
- display.sleep();
- }
-}
\ No newline at end of file
+++ /dev/null
-package era.mi.gui;
-
-import java.util.function.Consumer;
-
-import era.mi.gui.model.ViewModel;
-import era.mi.gui.modeladapter.LogicModelParameters;
-import era.mi.gui.modeladapter.ViewLogicModelAdapter;
-import era.mi.logic.timeline.Timeline;
-
-public class SimpleLogicUIStandalone
-{
- public static void executeVisualisation(Consumer<ViewModel> setupViewModel)
- {
- // setup view model
- ViewModel viewModel = new ViewModel();
- setupViewModel.accept(viewModel);
-
- // convert to logic model
- LogicModelParameters params = new LogicModelParameters();
- params.gateProcessTime = 50;
- params.wireTravelTime = 10;
- Timeline timeline = ViewLogicModelAdapter.convert(viewModel, params);
-
- // initialize UI and executer
- LogicUIStandaloneGUI ui = new LogicUIStandaloneGUI(viewModel);
- LogicExecuter exec = new LogicExecuter(timeline);
-
- // run it
- exec.startLiveExecution();
- ui.run();
- exec.stopLiveExecution();
- }
-
- private SimpleLogicUIStandalone()
- {
- throw new UnsupportedOperationException("No SimpleLogicUIStandalone instances");
- }
-}
\ No newline at end of file
+++ /dev/null
-package era.mi.gui.examples;
-
-import era.mi.gui.SimpleLogicUIStandalone;
-import era.mi.gui.model.ViewModel;
-import era.mi.gui.model.components.GUIManualSwitch;
-import era.mi.gui.model.components.GUINotGate;
-import era.mi.gui.model.components.GUIOrGate;
-import era.mi.gui.model.wires.GUIWire;
-import era.mi.gui.model.wires.WireCrossPoint;
-import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
-
-public class RSLatchExample
-{
- public static void main(String[] args)
- {
- SimpleLogicUIStandalone.executeVisualisation(RSLatchExample::createRSLatchExample);
- }
-
- @SuppressWarnings("unused") // for GUIWires being created
- public static void createRSLatchExample(ViewModel model)
- {
- GUIManualSwitch rIn = new GUIManualSwitch(model);
- rIn.moveTo(100, 100);
- GUIManualSwitch sIn = new GUIManualSwitch(model);
- sIn.moveTo(100, 200);
-
- GUIOrGate or1 = new GUIOrGate(model, 1);
- or1.moveTo(160, 102.5);
- new GUIWire(model, rIn.getOutputPin(), or1.getInputPins().get(0));
-
- GUIOrGate or2 = new GUIOrGate(model, 1);
- or2.moveTo(160, 192.5);
- new GUIWire(model, sIn.getOutputPin(), or2.getInputPins().get(1));
-
- GUINotGate not1 = new GUINotGate(model, 1);
- not1.moveTo(200, 107.5);
- new GUIWire(model, or1.getOutputPin(), not1.getInputPins().get(0));
-
- GUINotGate not2 = new GUINotGate(model, 1);
- not2.moveTo(200, 197.5);
- new GUIWire(model, or2.getOutputPin(), not2.getInputPins().get(0));
-
- WireCrossPoint p1 = new WireCrossPoint(model, 1);
- p1.moveTo(250, 112.5);
- new GUIWire(model, not1.getOutputPin(), p1.getPin());
- new GUIWire(model, p1.getPin(), or2.getInputPins().get(0), new Point(250, 130), new Point(140, 185), new Point(140, 197.5));
-
- WireCrossPoint p2 = new WireCrossPoint(model, 1);
- p2.moveTo(250, 202.5);
- new GUIWire(model, not2.getOutputPin(), p2.getPin());
- new GUIWire(model, p2.getPin(), or1.getInputPins().get(1), new Point(250, 185), new Point(140, 130), new Point(140, 117.5));
-
- WireCrossPoint o1 = new WireCrossPoint(model, 1);
- o1.moveTo(270, 112.5);
- new GUIWire(model, p1.getPin(), o1.getPin());
-
- WireCrossPoint o2 = new WireCrossPoint(model, 1);
- o2.moveTo(270, 202.5);
- new GUIWire(model, p2.getPin(), o2.getPin());
- }
-}
\ No newline at end of file
+++ /dev/null
-package era.mi.gui.model;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.function.Consumer;
-
-import era.mi.gui.model.components.GUIComponent;
-import era.mi.gui.model.wires.GUIWire;
-
-public class ViewModel
-{
- private final List<GUIComponent> components;
- private final List<GUIComponent> componentsUnmodifiable;
- private final List<GUIWire> wires;
- private final List<GUIWire> wiresUnmodifiable;
-
- private final List<Consumer<? super GUIComponent>> componentAddedListeners;
- private final List<Consumer<? super GUIComponent>> componentRemovedListeners;
- private final List<Consumer<? super GUIWire>> wireAddedListeners;
- private final List<Consumer<? super GUIWire>> wireRemovedListeners;
-
- public ViewModel()
- {
- components = new ArrayList<>();
- componentsUnmodifiable = Collections.unmodifiableList(components);
- wires = new ArrayList<>();
- wiresUnmodifiable = Collections.unmodifiableList(wires);
-
- componentAddedListeners = new ArrayList<>();
- componentRemovedListeners = new ArrayList<>();
- wireAddedListeners = new ArrayList<>();
- wireRemovedListeners = new ArrayList<>();
- }
-
- /**
- * Adds the given component to the list of components and calls all componentAddedListeners. Don't call this method from application
- * code as it is automatically called in GUIComponent::new.
- */
- public void componentCreated(GUIComponent component)
- {
- if (components.contains(component))
- throw new IllegalStateException("Don't add the same component twice!");
- components.add(component);
- callComponentAddedListeners(component);
- }
-
- /**
- * Removes the given component from the list of components and calls all componentRemovedListeners. Don't call this method from
- * application code as it is automatically called in GUIComponent::destroy.
- */
- public void componentDestroyed(GUIComponent component)
- {
- if (!components.contains(component))
- throw new IllegalStateException("Don't remove the same component twice!");
- components.remove(component);
- callComponentRemovedListeners(component);
- }
-
- /**
- * Adds the given component to the list of components and calls all componentAddedListeners. Don't call this method from application
- * code as it is automatically called in GUIComponent::new.
- */
- public void wireCreated(GUIWire wire)
- {
- if (wires.contains(wire))
- throw new IllegalStateException("Don't add the same wire twice!");
- wires.add(wire);
- callWireAddedListeners(wire);
- }
-
- /**
- * Removes the given component from the list of components and calls all componentRemovedListeners. Don't call this method from
- * application code as it is automatically called in GUIComponent::destroy.
- */
- public void wireDestroyed(GUIWire wire)
- {
- if (!wires.contains(wire))
- throw new IllegalStateException("Don't remove the same wire twice!");
- wires.remove(wire);
- callWireRemovedListeners(wire);
- }
-
- public List<GUIComponent> getComponents()
- {
- return componentsUnmodifiable;
- }
-
- public List<GUIWire> getWires()
- {
- return wiresUnmodifiable;
- }
-
- // @formatter:off
- public void addComponentAddedListener (Consumer<? super GUIComponent> listener){componentAddedListeners .add (listener);}
- public void addComponentRemovedListener (Consumer<? super GUIComponent> listener){componentRemovedListeners.add (listener);}
- public void addWireAddedListener (Consumer<? super GUIWire > listener){wireAddedListeners .add (listener);}
- public void addWireRemovedListener (Consumer<? super GUIWire > listener){wireRemovedListeners .add (listener);}
-
- public void removeComponentAddedListener (Consumer<? super GUIComponent> listener){componentAddedListeners .remove(listener);}
- public void removeComponentRemovedListener(Consumer<? super GUIComponent> listener){componentRemovedListeners.remove(listener);}
- public void removeWireAddedListener (Consumer<? super GUIWire > listener){wireAddedListeners .remove(listener);}
- public void removeWireRemovedListener (Consumer<? super GUIWire > listener){wireRemovedListeners .remove(listener);}
-
- private void callComponentAddedListeners (GUIComponent c) {componentAddedListeners .forEach(l -> l.accept(c));}
- private void callComponentRemovedListeners(GUIComponent c) {componentRemovedListeners.forEach(l -> l.accept(c));}
- private void callWireAddedListeners (GUIWire w ) {wireAddedListeners .forEach(l -> l.accept(w));}
- private void callWireRemovedListeners (GUIWire w ) {wireRemovedListeners .forEach(l -> l.accept(w));}
- // @formatter:on
-}
\ No newline at end of file
+++ /dev/null
-package era.mi.gui.model.components;
-
-import era.mi.gui.model.ViewModel;
-
-public class GUIAndGate extends SimpleRectangularGUIGate
-{
- public GUIAndGate(ViewModel model, int logicWidth)
- {
- super(model, logicWidth, "&", false);
- setInputCount(2);// TODO make variable
- }
-}
\ No newline at end of file
+++ /dev/null
-package era.mi.gui.model.components;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.function.Consumer;
-
-import era.mi.gui.model.ViewModel;
-import era.mi.gui.model.wires.Pin;
-import net.haspamelodica.swt.helper.gcs.GeneralGC;
-import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
-
-public abstract class GUIComponent
-{
- protected final ViewModel model;
- private final Rectangle bounds;
- private final List<Pin> pins;
- protected final List<Pin> pinsUnmodifiable;
-
- private final List<Consumer<? super GUIComponent>> componentLookChangedListeners;
- private final List<Consumer<? super GUIComponent>> componentMovedListeners;
- private final List<Consumer<? super Pin>> pinAddedListeners;
- private final List<Consumer<? super Pin>> pinRemovedListeners;
-
- public GUIComponent(ViewModel model)
- {
- this.model = model;
- this.bounds = new Rectangle(0, 0, 0, 0);
- this.pins = new ArrayList<>();
- this.pinsUnmodifiable = Collections.unmodifiableList(pins);
-
- this.componentLookChangedListeners = new ArrayList<>();
- this.componentMovedListeners = new ArrayList<>();
- this.pinAddedListeners = new ArrayList<>();
- this.pinRemovedListeners = new ArrayList<>();
-
- model.componentCreated(this);
- }
-
- public void destroy()
- {
- pins.forEach(p -> pinRemovedListeners.forEach(l -> l.accept(p)));
- model.componentDestroyed(this);
- }
-
- public void moveTo(double x, double y)
- {
- bounds.x = x;
- bounds.y = y;
- callComponentMovedListeners();
- }
-
- /**
- * Returns the bounds of this component. Used for calculating which component is clicked.
- */
- public Rectangle getBounds()
- {
- return new Rectangle(bounds.x, bounds.y, bounds.width, bounds.height);
- }
-
- /**
- * Called when this component is clicked. Absolute coordinates of the click are given. Returns true if this component consumed this
- * click.
- */
- @SuppressWarnings({ "static-method", "unused" }) // this method is inteded to be overridden
- public boolean clicked(double x, double y)
- {
- return false;
- }
-
- /**
- * Returns a list of pins of this component.
- */
- public List<Pin> getPins()
- {
- return pinsUnmodifiable;
- }
-
- // @formatter:off
- public void addComponentLookChangedListener (Consumer<? super GUIComponent> listener) {componentLookChangedListeners.add (listener);}
- public void addComponentMovedListener (Consumer<? super GUIComponent> listener) {componentMovedListeners .add (listener);}
- public void addPinAddedListener (Consumer<? super Pin > listener) {pinAddedListeners .add (listener);}
- public void addPinRemovedListener (Consumer<? super Pin > listener) {pinRemovedListeners .add (listener);}
-
- public void removeComponentLookChangedListener(Consumer<? super GUIComponent> listener) {componentLookChangedListeners.remove(listener);}
- public void removeComponentMovedListener (Consumer<? super GUIComponent> listener) {componentMovedListeners .remove(listener);}
- public void removePinAddedListener (Consumer<? super Pin > listener) {pinAddedListeners .remove(listener);}
- public void removePinRemovedListener (Consumer<? super Pin > listener) {pinRemovedListeners .remove(listener);}
-
- protected void callComponentLookChangedListeners( ) {componentLookChangedListeners.forEach(l -> l.accept(this));}
- private void callComponentMovedListeners ( ) {componentMovedListeners .forEach(l -> l.accept(this));}
- private void callPinAddedListeners (Pin p) {pinAddedListeners .forEach(l -> l.accept(p ));}
- private void callPinRemovedListeners (Pin p) {pinRemovedListeners .forEach(l -> l.accept(p ));}
- // @form atter:on
-
- /**
- * Render this component to the given gc.
- */
- public abstract void render(GeneralGC gc, Rectangle visibleRegion);
-
- protected void setSize(double width, double height)
- {
- bounds.width = width;
- bounds.height = height;
- callComponentLookChangedListeners();
- }
-
- protected void addPin(Pin pin)
- {
- pins.add(pin);
- callPinAddedListeners(pin);
- }
-
- protected void removePin(Pin pin)
- {
- pins.remove(pin);
- callPinRemovedListeners(pin);
- }
-}
\ No newline at end of file
+++ /dev/null
-package era.mi.gui.model.components;
-
-import era.mi.gui.model.ViewModel;
-import era.mi.gui.model.wires.Pin;
-import era.mi.logic.components.ManualSwitch;
-import era.mi.logic.types.BitVectorFormatter;
-import era.mi.logic.wires.Wire.ReadEnd;
-import net.haspamelodica.swt.helper.gcs.GeneralGC;
-import net.haspamelodica.swt.helper.swtobjectwrappers.Font;
-import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
-import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
-
-public class GUIManualSwitch extends GUIComponent
-{
- private static final double width = 20;
- private static final double height = 15;
- private static final double fontHeight = 5;
-
- private final Pin outputPin;
-
- private ManualSwitch logicSwitch;
- private ReadEnd end;
-
- public GUIManualSwitch(ViewModel model)
- {
- super(model);
- setSize(width, height);
- addPin(this.outputPin = new Pin(this, 1, width, height / 2));
- }
-
- @Override
- public void render(GeneralGC gc, Rectangle visibleRegion)
- {
- double posX = getBounds().x;
- double posY = getBounds().y;
-
- gc.drawRectangle(posX, posY, width, height);
- String label = BitVectorFormatter.formatValueAsString(end);
- Font oldFont = gc.getFont();
- Font labelFont = new Font(oldFont.getName(), fontHeight, oldFont.getStyle());
- gc.setFont(labelFont);
- Point textExtent = gc.textExtent(label);
- gc.drawText(label, posX + (width - textExtent.x) / 2, posY + (height - textExtent.y) / 2, true);
- gc.setFont(oldFont);
- }
-
- public void setLogicModelBinding(ManualSwitch logicSwitch, ReadEnd end)
- {
- this.logicSwitch = logicSwitch;
- this.end = end;
- // TODO when ManualSwitch supports it, add listeners
- end.addObserver((i, o) -> callComponentLookChangedListeners());
- }
-
- @Override
- public boolean clicked(double x, double y)
- {
- if (logicSwitch != null)
- logicSwitch.toggle();
- return true;
- }
-
- public Pin getOutputPin()
- {
- return outputPin;
- }
-}
\ No newline at end of file
+++ /dev/null
-package era.mi.gui.model.components;
-
-import era.mi.gui.model.ViewModel;
-
-public class GUINotGate extends SimpleRectangularGUIGate
-{
- public GUINotGate(ViewModel model, int logicWidth)
- {
- super(model, logicWidth, "1", true);
- setInputCount(1);
- }
-}
\ No newline at end of file
+++ /dev/null
-package era.mi.gui.model.components;
-
-import era.mi.gui.model.ViewModel;
-
-public class GUIOrGate extends SimpleRectangularGUIGate
-{
- public GUIOrGate(ViewModel model, int logicWidth)
- {
- super(model, logicWidth, "\u22651", false);// ">=1"
- setInputCount(2);
- }
-}
\ No newline at end of file
+++ /dev/null
-package era.mi.gui.model.components;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import era.mi.gui.model.ViewModel;
-import era.mi.gui.model.wires.MovablePin;
-import era.mi.gui.model.wires.Pin;
-import net.haspamelodica.swt.helper.gcs.GeneralGC;
-import net.haspamelodica.swt.helper.swtobjectwrappers.Font;
-import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
-import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
-
-public class SimpleRectangularGUIGate extends GUIComponent
-{
- private static final double width = 20;
- private static final double pinDistance = 10;
- private static final double fontHeight = 5;
- private static final double invertedCircleDiam = 3.5;
-
- private final String label;
- protected final int logicWidth;
- private final boolean isInverted;
- private final double rectWidth;
-
- private MovablePin outputPin;
- private final List<Pin> inputPins;
- private final List<Pin> inputPinsUnmodifiable;
-
- protected SimpleRectangularGUIGate(ViewModel model, int logicWidth, String label, boolean isInverted)
- {
- super(model);
- this.label = label;
- this.logicWidth = logicWidth;
- this.isInverted = isInverted;
- this.rectWidth = width - (isInverted ? invertedCircleDiam : 0);
- this.outputPin = new MovablePin(this, logicWidth, width, 0);
- addPin(outputPin);
- this.inputPins = new ArrayList<>();
- this.inputPinsUnmodifiable = Collections.unmodifiableList(inputPins);
- setInputCount(1);
- }
-
- protected void setInputCount(int inputCount)
- {
- int oldInputCount = inputPins.size();
- setSize(width, inputCount * pinDistance);
- if (oldInputCount > inputCount)
- while (inputPins.size() > inputCount)
- removePin(inputPins.get(inputCount));
- else if (oldInputCount < inputCount)
- for (int i = oldInputCount; i < inputCount; i++)
- {
- Pin pin = new Pin(this, logicWidth, 0, pinDistance / 2 + i * pinDistance);
- inputPins.add(pin);
- addPin(pin);
- }
- outputPin.setRelPos(width, inputCount * pinDistance / 2);
- }
-
- public Pin getOutputPin()
- {
- return outputPin;
- }
-
- public List<Pin> getInputPins()
- {
- return inputPinsUnmodifiable;
- }
-
- @Override
- public void render(GeneralGC gc, Rectangle visibleRegion)
- {
- double posX = getBounds().x;
- double posY = getBounds().y;
-
- double height = inputPins.size() * pinDistance;
- gc.drawRectangle(posX, posY, rectWidth, height);
- Font oldFont = gc.getFont();
- Font labelFont = new Font(oldFont.getName(), fontHeight, oldFont.getStyle());
- gc.setFont(labelFont);
- Point textExtent = gc.textExtent(label);
- gc.drawText(label, posX + (rectWidth - textExtent.x) / 2, posY + (height - textExtent.y) / 2, true);
- gc.setFont(oldFont);
- if (isInverted)
- gc.drawOval(posX + rectWidth, posY + (height - invertedCircleDiam) / 2, invertedCircleDiam, invertedCircleDiam);
- }
-}
\ No newline at end of file
+++ /dev/null
-package era.mi.gui.model.wires;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.Consumer;
-
-import era.mi.gui.ColorHelper;
-import era.mi.gui.model.ViewModel;
-import era.mi.logic.types.BitVectorFormatter;
-import era.mi.logic.wires.Wire.ReadEnd;
-import net.haspamelodica.swt.helper.gcs.GeneralGC;
-import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
-
-public class GUIWire
-{
- private final ViewModel model;
- public final int logicWidth;
- private Pin pin1;
- private Pin pin2;
- private double[] path;
-
- private final List<Consumer<? super GUIWire>> wireLookChangedListeners;
-
- private ReadEnd end;
-
- public GUIWire(ViewModel model, Pin pin1, Pin pin2, Point... path)
- {
- this.model = model;
- this.logicWidth = pin1.logicWidth;
- if (pin2.logicWidth != pin1.logicWidth)
- throw new IllegalArgumentException("Can't connect pins of different logic width");
- this.path = new double[path.length * 2 + 4];
- for (int srcI = 0, dstI = 2; srcI < path.length; srcI++, dstI += 2)
- {
- this.path[dstI + 0] = path[srcI].x;
- this.path[dstI + 1] = path[srcI].y;
- }
-
- this.pin1 = pin1;
- this.pin2 = pin2;
-
- wireLookChangedListeners = new ArrayList<>();
-
- pin1.addPinMovedListener(p -> pin1Moved());
- pin2.addPinMovedListener(p -> pin2Moved());
- pin1Moved();
- pin2Moved();
-
- model.wireCreated(this);
- }
-
- private void pin1Moved()
- {
- Point pos = pin1.getPos();
- this.path[0] = pos.x;
- this.path[1] = pos.y;
- }
-
- private void pin2Moved()
- {
- Point pos = pin2.getPos();
- this.path[this.path.length - 2] = pos.x;
- this.path[this.path.length - 1] = pos.y;
- }
-
- public void destroy()
- {
- model.wireDestroyed(this);
- }
-
- public void render(GeneralGC gc)
- {
- ColorHelper.executeWithDifferentForeground(gc, BitVectorFormatter.formatAsColor(end), () -> gc.drawPolyline(path));
- }
-
- public void setLogicModelBinding(ReadEnd end)
- {
- this.end = end;
- end.addObserver((i, o) -> callWireLookChangedListeners());
- }
-
- public Pin getPin1()
- {
- return pin1;
- }
-
- public Pin getPin2()
- {
- return pin2;
- }
-
- // @formatter:off
- public void addWireLookChangedListener (Consumer<? super GUIWire> listener) {wireLookChangedListeners.add (listener);}
-
- public void removeWireLookChangedListener(Consumer<? super GUIWire> listener) {wireLookChangedListeners.remove(listener);}
-
- private void callWireLookChangedListeners() {wireLookChangedListeners.forEach(l -> l.accept(this));}
- // @formatter:on
-
-}
\ No newline at end of file
+++ /dev/null
-package era.mi.gui.model.wires;
-
-import era.mi.gui.model.components.GUIComponent;
-
-public class MovablePin extends Pin
-{
- public MovablePin(GUIComponent component, int logicWidth, double relX, double relY)
- {
- super(component, logicWidth, relX, relY);
- }
-
- @Override
- public void setRelPos(double relX, double relY)
- {
- super.setRelPos(relX, relY);
- }
-}
\ No newline at end of file
+++ /dev/null
-package era.mi.gui.model.wires;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.Consumer;
-
-import era.mi.gui.model.components.GUIComponent;
-import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
-import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
-
-public class Pin
-{
- public final GUIComponent component;
- public final int logicWidth;
-
- protected double relX;
- protected double relY;
-
- private final List<Consumer<? super Pin>> pinMovedListeners;
-
- public Pin(GUIComponent component, int logicWidth, double relX, double relY)
- {
- this.component = component;
- this.logicWidth = logicWidth;
- this.relX = relX;
- this.relY = relY;
-
- this.pinMovedListeners = new ArrayList<>();
-
- component.addComponentMovedListener(c -> callPinMovedListeners());
- }
-
- public double getRelX()
- {
- return relX;
- }
-
- public double getRelY()
- {
- return relY;
- }
-
- public Point getRelPos()
- {
- return new Point(relX, relY);
- }
-
- public Point getPos()
- {
- Rectangle componentBounds = component.getBounds();
- return new Point(relX + componentBounds.x, relY + componentBounds.y);
- }
-
- // @formatter:off
- public void addPinMovedListener (Consumer<? super Pin> listener){pinMovedListeners.add (listener);}
-
- public void removePinMovedListener(Consumer<? super Pin> listener){pinMovedListeners.remove(listener);}
-
- private void callPinMovedListeners() {pinMovedListeners.forEach(l -> l.accept(this));}
- // @formatter:on
-
- protected void setRelPos(double relX, double relY)
- {
- this.relX = relX;
- this.relY = relY;
- callPinMovedListeners();
- }
-}
\ No newline at end of file
+++ /dev/null
-package era.mi.gui.model.wires;
-
-import era.mi.gui.ColorHelper;
-import era.mi.gui.model.ViewModel;
-import era.mi.gui.model.components.GUIComponent;
-import era.mi.logic.types.BitVectorFormatter;
-import era.mi.logic.wires.Wire.ReadEnd;
-import net.haspamelodica.swt.helper.gcs.GeneralGC;
-import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
-
-public class WireCrossPoint extends GUIComponent
-{
- private final Pin pin;
-
- private ReadEnd end;
- private final int logicWidth;
-
- public WireCrossPoint(ViewModel model, int logicWidth)
- {
- super(model);
- this.logicWidth = logicWidth;
- setSize(0, 0);
- addPin(this.pin = new Pin(this, logicWidth, 0, 0));
- }
-
- @Override
- public void render(GeneralGC gc, Rectangle visibleRegion)
- {
- Rectangle bounds = getBounds();
- ColorHelper.executeWithDifferentBackground(gc, BitVectorFormatter.formatAsColor(end),
- () -> gc.fillOval(bounds.x - 1, bounds.y - 1, 2, 2));
- }
-
- public void setLogicModelBinding(ReadEnd end)
- {
- this.end = end;
- end.addObserver((i, o) -> callComponentLookChangedListeners());
- }
-
- public int getLogicWidth()
- {
- return logicWidth;
- }
-
- public Pin getPin()
- {
- return pin;
- }
-}
\ No newline at end of file
+++ /dev/null
-package era.mi.gui.modeladapter;
-
-public class LogicModelParameters
-{
- public int wireTravelTime;
- public int gateProcessTime;
-}
\ No newline at end of file
+++ /dev/null
-package era.mi.gui.modeladapter;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-import era.mi.gui.model.ViewModel;
-import era.mi.gui.model.components.GUIAndGate;
-import era.mi.gui.model.components.GUIComponent;
-import era.mi.gui.model.components.GUINotGate;
-import era.mi.gui.model.components.GUIOrGate;
-import era.mi.gui.model.wires.GUIWire;
-import era.mi.gui.model.wires.Pin;
-import era.mi.gui.model.wires.WireCrossPoint;
-import era.mi.gui.modeladapter.componentadapters.ComponentAdapter;
-import era.mi.gui.modeladapter.componentadapters.ManualSwitchAdapter;
-import era.mi.gui.modeladapter.componentadapters.SimpleGateAdapter;
-import era.mi.logic.components.Component;
-import era.mi.logic.components.gates.AndGate;
-import era.mi.logic.components.gates.NotGate;
-import era.mi.logic.components.gates.OrGate;
-import era.mi.logic.timeline.Timeline;
-import era.mi.logic.wires.Wire;
-import era.mi.logic.wires.Wire.ReadEnd;
-
-public class ViewLogicModelAdapter
-{
- private final static Map<Class<? extends GUIComponent>, ComponentAdapter<? extends GUIComponent>> componentAdapters;
- static
- {
- Set<ComponentAdapter<? extends GUIComponent>> componentAdaptersModifiable = new HashSet<>();
- componentAdaptersModifiable.add(new SimpleGateAdapter<>(GUIOrGate.class, OrGate::new));
- componentAdaptersModifiable.add(new SimpleGateAdapter<>(GUIAndGate.class, AndGate::new));
- componentAdaptersModifiable.add(new SimpleGateAdapter<>(GUINotGate.class, (t, p, o, i) -> new NotGate(t, p, i[0], o)));
- componentAdaptersModifiable.add(new ManualSwitchAdapter());
- // TODO list all "primitive" adapters here
- componentAdapters = Collections.unmodifiableMap(
- componentAdaptersModifiable.stream().collect(Collectors.toMap(ComponentAdapter::getSupportedClass, Function.identity())));
- }
-
- public static Timeline convert(ViewModel viewModel, LogicModelParameters params)
- {
- // TODO replace Timeline with LogicModel as soon as it exists
- Timeline timeline = new Timeline(10);
-
- Map<Pin, Wire> logicWiresPerPin = convertWires(
- viewModel.getComponents().stream().flatMap(component -> component.getPins().stream()).collect(Collectors.toSet()),
- viewModel.getWires(), params, timeline);
- Map<Pin, Wire> logicWiresPerPinUnmodifiable = Collections.unmodifiableMap(logicWiresPerPin);
-
- Map<GUIComponent, Component> oneToOneComponents = new HashMap<>();
- for (GUIComponent guiComp : viewModel.getComponents())
- {
- if (!(guiComp instanceof WireCrossPoint))
- oneToOneComponents.put(guiComp, createAndLinkComponent(timeline, params, guiComp, logicWiresPerPinUnmodifiable,
- componentAdapters.get(guiComp.getClass())));
- else
- {
- WireCrossPoint guiCompCasted = (WireCrossPoint) guiComp;
- guiCompCasted.setLogicModelBinding(logicWiresPerPin.get(guiCompCasted.getPin()).createReadOnlyEnd());
- }
- }
-
- // TODO handle complex components
-
- List<Component> logicComponents = new ArrayList<>();
- logicComponents.addAll(oneToOneComponents.values());
-
- return timeline;
- }
-
- private static Map<Pin, Wire> convertWires(Set<Pin> allPins, List<GUIWire> wires, LogicModelParameters params, Timeline timeline)
- {
- Map<Pin, Set<Pin>> connectedPinGroups = getConnectedPinGroups(allPins, wires);
- Map<Pin, Wire> logicWiresPerPin = createLogicWires(params, timeline, connectedPinGroups);
- setGUIWiresLogicModelBinding(wires, logicWiresPerPin);
- return logicWiresPerPin;
- }
-
- private static Map<Pin, Wire> createLogicWires(LogicModelParameters params, Timeline timeline, Map<Pin, Set<Pin>> connectedPinGroups)
- {
- Map<Pin, Wire> logicWiresPerPin = new HashMap<>();
- Map<Set<Pin>, Wire> logicWiresPerPinGroup = new HashMap<>();
- for (Entry<Pin, Set<Pin>> e : connectedPinGroups.entrySet())
- logicWiresPerPin.put(e.getKey(), logicWiresPerPinGroup.computeIfAbsent(e.getValue(),
- set -> new Wire(timeline, e.getKey().logicWidth, params.wireTravelTime)));
- return logicWiresPerPin;
- }
-
- private static void setGUIWiresLogicModelBinding(List<GUIWire> wires, Map<Pin, Wire> logicWiresPerPin)
- {
- Map<Wire, ReadEnd> guiWireSharedReadEnd = logicWiresPerPin.values().stream().distinct()
- .collect(Collectors.toMap(Function.identity(), Wire::createReadOnlyEnd));
- for (GUIWire guiWire : wires)
- guiWire.setLogicModelBinding(guiWireSharedReadEnd.get(logicWiresPerPin.get(guiWire.getPin1())));
- }
-
- private static Map<Pin, Set<Pin>> getConnectedPinGroups(Set<Pin> allPins, List<GUIWire> wires)
- {
- Map<Pin, Set<Pin>> connectedPinsPerPin = new HashMap<>();
-
- for (Pin p : allPins)
- {
- HashSet<Pin> connectedPins = new HashSet<>();
- connectedPins.add(p);
- connectedPinsPerPin.put(p, connectedPins);
- }
-
- wires.forEach(wire ->
- {
- Pin pin1 = wire.getPin1();
- Pin pin2 = wire.getPin2();
-
- Set<Pin> pin1ConnectedPins = connectedPinsPerPin.get(pin1);
- Set<Pin> pin2ConnectedPins = connectedPinsPerPin.get(pin2);
-
- pin1ConnectedPins.addAll(pin2ConnectedPins);
- pin1ConnectedPins.add(pin1);
- pin1ConnectedPins.add(pin2);
-
- pin2ConnectedPins.forEach(pin -> connectedPinsPerPin.put(pin, pin1ConnectedPins));
- });
- return connectedPinsPerPin;
- }
-
- @SuppressWarnings("unchecked")
- private static <G extends GUIComponent> Component createAndLinkComponent(Timeline timeline, LogicModelParameters params,
- GUIComponent guiComponent, Map<Pin, Wire> logicWiresPerPin, ComponentAdapter<G> adapter)
- {
- if (adapter == null)
- throw new IllegalArgumentException("Unknown component class: " + guiComponent.getClass());
- return adapter.createAndLinkComponent(timeline, params, (G) guiComponent, logicWiresPerPin);
- }
-
- private ViewLogicModelAdapter()
- {
- throw new UnsupportedOperationException("No ViewLogicModelConverter instances");
- }
-}
\ No newline at end of file
+++ /dev/null
-package era.mi.gui.modeladapter.componentadapters;
-
-import java.util.Map;
-
-import era.mi.gui.model.components.GUIComponent;
-import era.mi.gui.model.wires.Pin;
-import era.mi.gui.modeladapter.LogicModelParameters;
-import era.mi.logic.components.Component;
-import era.mi.logic.timeline.Timeline;
-import era.mi.logic.wires.Wire;
-
-public interface ComponentAdapter<G extends GUIComponent>
-{
- public Class<G> getSupportedClass();
-
- public Component createAndLinkComponent(Timeline timeline, LogicModelParameters params, G guiComponent,
- Map<Pin, Wire> logicWiresPerPin);
-}
\ No newline at end of file
+++ /dev/null
-package era.mi.gui.modeladapter.componentadapters;
-
-import java.util.Map;
-
-import era.mi.gui.model.components.GUIManualSwitch;
-import era.mi.gui.model.wires.Pin;
-import era.mi.gui.modeladapter.LogicModelParameters;
-import era.mi.logic.components.Component;
-import era.mi.logic.components.ManualSwitch;
-import era.mi.logic.timeline.Timeline;
-import era.mi.logic.wires.Wire;
-import era.mi.logic.wires.Wire.ReadWriteEnd;
-
-public class ManualSwitchAdapter implements ComponentAdapter<GUIManualSwitch>
-{
- @Override
- public Class<GUIManualSwitch> getSupportedClass()
- {
- return GUIManualSwitch.class;
- }
-
- @Override
- public Component createAndLinkComponent(Timeline timeline, LogicModelParameters params, GUIManualSwitch guiComponent,
- Map<Pin, Wire> logicWiresPerPin)
- {
- ReadWriteEnd end = logicWiresPerPin.get(guiComponent.getOutputPin()).createReadWriteEnd();
- ManualSwitch manualSwitch = new ManualSwitch(timeline, end);
- guiComponent.setLogicModelBinding(manualSwitch, end);
- return manualSwitch;
- }
-}
\ No newline at end of file
+++ /dev/null
-package era.mi.gui.modeladapter.componentadapters;
-
-import java.util.List;
-import java.util.Map;
-
-import era.mi.gui.model.components.SimpleRectangularGUIGate;
-import era.mi.gui.model.wires.Pin;
-import era.mi.gui.modeladapter.LogicModelParameters;
-import era.mi.logic.components.Component;
-import era.mi.logic.timeline.Timeline;
-import era.mi.logic.wires.Wire;
-import era.mi.logic.wires.Wire.ReadEnd;
-import era.mi.logic.wires.Wire.ReadWriteEnd;
-
-public class SimpleGateAdapter<G extends SimpleRectangularGUIGate> implements ComponentAdapter<G>
-{
- private final Class<G> supportedClass;
- private final ComponentConstructor constructor;
-
- public SimpleGateAdapter(Class<G> supportedClass, ComponentConstructor constructor)
- {
- this.supportedClass = supportedClass;
- this.constructor = constructor;
- }
-
- @Override
- public Class<G> getSupportedClass()
- {
- return supportedClass;
- }
-
- @Override
- public Component createAndLinkComponent(Timeline timeline, LogicModelParameters params, G guiComponent, Map<Pin, Wire> logicWiresPerPin)
- {
- ReadWriteEnd out = logicWiresPerPin.get(guiComponent.getOutputPin()).createReadWriteEnd();
- List<Pin> inputPins = guiComponent.getInputPins();
- ReadEnd[] ins = new ReadEnd[inputPins.size()];
- for (int i = 0; i < inputPins.size(); i++)
- ins[i] = logicWiresPerPin.get(inputPins.get(i)).createReadOnlyEnd();
- return constructor.newComponent(timeline, params.gateProcessTime, out, ins);
- }
-
- public static interface ComponentConstructor
- {
- public Component newComponent(Timeline timeline, int processTime, ReadWriteEnd out, ReadEnd[] ins);
- }
-
-}
\ No newline at end of file
--- /dev/null
+package mograsim.logic.ui;
+
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Device;
+
+import mograsim.logic.core.types.ColorDefinition;
+import mograsim.logic.core.types.ColorDefinition.BuiltInColor;
+import net.haspamelodica.swt.helper.gcs.GeneralGC;
+
+//TODO replace with a proper ColorManager
+public class ColorHelper
+{
+ public static void executeWithDifferentForeground(GeneralGC gc, ColorDefinition col, Runnable exec)
+ {
+ executeWithDifferentColor(gc.getDevice(), col, gc::getForeground, gc::setForeground, exec);
+ }
+
+ public static void executeWithDifferentBackground(GeneralGC gc, ColorDefinition col, Runnable exec)
+ {
+ executeWithDifferentColor(gc.getDevice(), col, gc::getBackground, gc::setBackground, exec);
+ }
+
+ private static void executeWithDifferentColor(Device device, ColorDefinition col, Supplier<Color> getColor, Consumer<Color> setColor,
+ Runnable exec)
+ {
+ Color oldColor = getColor.get();
+ boolean isNoSystemColor = col.builtInColor == null;
+ Color newColor;
+ if (isNoSystemColor)
+ newColor = new Color(device, col.r, col.g, col.b);
+ else
+ newColor = device.getSystemColor(ColorHelper.toSWTColorConstant(col.builtInColor));
+ setColor.accept(newColor);
+
+ exec.run();
+
+ setColor.accept(oldColor);
+ if (isNoSystemColor)
+ newColor.dispose();
+ }
+
+ public static int toSWTColorConstant(BuiltInColor col)
+ {
+ switch (col)
+ {
+ case COLOR_BLACK:
+ return SWT.COLOR_BLACK;
+ case COLOR_BLUE:
+ return SWT.COLOR_BLUE;
+ case COLOR_CYAN:
+ return SWT.COLOR_CYAN;
+ case COLOR_DARK_BLUE:
+ return SWT.COLOR_DARK_BLUE;
+ case COLOR_DARK_CYAN:
+ return SWT.COLOR_DARK_CYAN;
+ case COLOR_DARK_GRAY:
+ return SWT.COLOR_DARK_GRAY;
+ case COLOR_DARK_GREEN:
+ return SWT.COLOR_DARK_GREEN;
+ case COLOR_DARK_MAGENTA:
+ return SWT.COLOR_DARK_MAGENTA;
+ case COLOR_DARK_RED:
+ return SWT.COLOR_DARK_RED;
+ case COLOR_DARK_YELLOW:
+ return SWT.COLOR_DARK_YELLOW;
+ case COLOR_GRAY:
+ return SWT.COLOR_GRAY;
+ case COLOR_GREEN:
+ return SWT.COLOR_GREEN;
+ case COLOR_MAGENTA:
+ return SWT.COLOR_MAGENTA;
+ case COLOR_RED:
+ return SWT.COLOR_RED;
+ case COLOR_WHITE:
+ return SWT.COLOR_WHITE;
+ case COLOR_YELLOW:
+ return SWT.COLOR_YELLOW;
+ default:
+ throw new IllegalArgumentException("Unknown enum constant: " + col);
+ }
+ }
+
+ private ColorHelper()
+ {
+ throw new UnsupportedOperationException("No instances of ColorHelper");
+ }
+}
\ No newline at end of file
--- /dev/null
+package mograsim.logic.ui;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+
+import mograsim.logic.core.timeline.Timeline;
+
+//TODO maybe move to logic core?
+public class LogicExecuter
+{
+ // TODO replace with LogicModel when it exists
+ private final Timeline timeline;
+
+ private final AtomicBoolean shouldBeRunningLive;
+ private final AtomicBoolean isRunningLive;
+ private final AtomicLong nextExecSimulTime;
+ private final Thread simulationThread;
+
+ public LogicExecuter(Timeline timeline)
+ {
+ this.timeline = timeline;
+
+ timeline.setTimeFunction(System::currentTimeMillis);
+ shouldBeRunningLive = new AtomicBoolean();
+ isRunningLive = new AtomicBoolean();
+ nextExecSimulTime = new AtomicLong();
+ simulationThread = new Thread(() ->
+ {
+ isRunningLive.set(true);
+ synchronized (isRunningLive)
+ {
+ isRunningLive.notify();
+ }
+ while (shouldBeRunningLive.get())
+ {
+ // always execute to keep timeline from "hanging behind" for too long
+ long current = System.currentTimeMillis();
+ timeline.executeUntil(timeline.laterThan(current), current + 10);
+ long sleepTime;
+ if (timeline.hasNext())
+ sleepTime = timeline.nextEventTime() - current;
+ else
+ sleepTime = 10000;
+ try
+ {
+ nextExecSimulTime.set(current + sleepTime);
+ if (sleepTime > 0)
+ Thread.sleep(sleepTime);
+ }
+ catch (@SuppressWarnings("unused") InterruptedException e)
+ {// do nothing; it is normal execution flow to be interrupted
+ }
+ }
+ isRunningLive.set(false);
+ synchronized (isRunningLive)
+ {
+ isRunningLive.notify();
+ }
+ });
+ timeline.addEventAddedListener(event ->
+ {
+ if (isRunningLive.get())
+ if (Timeline.timeCmp(event.getTiming(), nextExecSimulTime.get()) < 0)
+ simulationThread.interrupt();
+ });
+ }
+
+ public void executeNextStep()
+ {
+ timeline.executeNext();
+ }
+
+ public synchronized void startLiveExecution()
+ {
+ if (shouldBeRunningLive.get())
+ return;
+ shouldBeRunningLive.set(true);
+ simulationThread.start();
+ waitForIsRunning(true);
+ }
+
+ public synchronized void stopLiveExecution()
+ {
+ if (!shouldBeRunningLive.get())
+ return;
+ shouldBeRunningLive.set(false);
+ simulationThread.interrupt();
+ waitForIsRunning(false);
+ }
+
+ private void waitForIsRunning(boolean expectedState)
+ {
+ while (isRunningLive.get() ^ expectedState)
+ try
+ {
+ synchronized (isRunningLive)
+ {
+ isRunningLive.wait();
+ }
+ }
+ catch (@SuppressWarnings("unused") InterruptedException e)
+ {// no need to do anything
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+package mograsim.logic.ui;
+
+import java.util.function.Consumer;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+
+import mograsim.logic.ui.model.ViewModel;
+import mograsim.logic.ui.model.components.GUIComponent;
+import mograsim.logic.ui.model.wires.GUIWire;
+import mograsim.logic.ui.model.wires.Pin;
+import net.haspamelodica.swt.helper.gcs.GeneralGC;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
+import net.haspamelodica.swt.helper.zoomablecanvas.ZoomableCanvas;
+
+/**
+ * Simulation visualizer canvas.
+ *
+ * @author Daniel Kirschten
+ */
+public class LogicUICanvas extends ZoomableCanvas
+{
+ private static final boolean DRAW_PINS = false;
+
+ private final ViewModel model;
+
+ public LogicUICanvas(Composite parent, int style, ViewModel model)
+ {
+ super(parent, style);
+
+ this.model = model;
+
+ Consumer<Object> redrawConsumer = o -> redrawThreadsafe();
+ Consumer<Pin> pinAddedListener = p ->
+ {
+ p.addPinMovedListener(redrawConsumer);
+ redrawThreadsafe();
+ };
+ Consumer<Pin> pinRemovedListener = p ->
+ {
+ p.removePinMovedListener(redrawConsumer);
+ redrawThreadsafe();
+ };
+ Consumer<? super GUIComponent> componentAddedListener = c ->
+ {
+ c.addComponentLookChangedListener(redrawConsumer);
+ c.addComponentMovedListener(redrawConsumer);
+ c.addPinAddedListener(pinAddedListener);
+ c.addPinRemovedListener(pinRemovedListener);
+ redrawThreadsafe();
+ };
+ model.addComponentAddedListener(componentAddedListener);
+ model.getComponents().forEach(componentAddedListener);
+ model.addComponentRemovedListener(c ->
+ {
+ c.removeComponentLookChangedListener(redrawConsumer);
+ c.removeComponentMovedListener(redrawConsumer);
+ c.removePinAddedListener(pinAddedListener);
+ c.removePinRemovedListener(pinRemovedListener);
+ redrawThreadsafe();
+ });
+ Consumer<? super GUIWire> wireAddedListener = w ->
+ {
+ w.addWireLookChangedListener(redrawConsumer);
+ redrawThreadsafe();
+ };
+ model.addWireAddedListener(wireAddedListener);
+ model.getWires().forEach(wireAddedListener);
+ model.addWireRemovedListener(w ->
+ {
+ w.removeWireLookChangedListener(redrawConsumer);
+ redrawThreadsafe();
+ });
+
+ addZoomedRenderer(gc ->
+ {
+ gc.setLineWidth(.5);
+ model.getWires().forEach(w -> w.render(gc));
+ Rectangle visibleRegion = new Rectangle(offX, offY, gW / zoom, gH / zoom);
+ model.getComponents().forEach(c -> drawComponent(gc, c, visibleRegion));
+ });
+ addListener(SWT.MouseDown, this::mouseDown);
+ }
+
+ private void drawComponent(GeneralGC gc, GUIComponent component, Rectangle visibleRegion)
+ {
+ component.render(gc, visibleRegion);
+ if (DRAW_PINS)
+ {
+ gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_DARK_CYAN));
+ for (Pin p : component.getPins())
+ {
+ Point pos = p.getPos();
+ gc.fillOval(pos.x - 1, pos.y - 1, 2, 2);
+ }
+ }
+ }
+
+ private void mouseDown(Event e)
+ {
+ if (e.button == 1)
+ {
+ Point click = displayToWorldCoords(e.x, e.y);
+ for (GUIComponent component : model.getComponents())
+ if (component.getBounds().contains(click) && component.clicked(click.x, click.y))
+ {
+ redraw();
+ break;
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+package mograsim.logic.ui;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+
+import mograsim.logic.ui.model.ViewModel;
+import mograsim.logic.ui.modeladapter.LogicModelParameters;
+import net.haspamelodica.swt.helper.zoomablecanvas.helper.ZoomableCanvasOverlay;
+import net.haspamelodica.swt.helper.zoomablecanvas.helper.ZoomableCanvasUserInput;
+
+/**
+ * Standalone simulation visualizer graphical user interface.
+ *
+ * @author Daniel Kirschten
+ */
+public class LogicUIStandaloneGUI
+{
+ private final Display display;
+ private final Shell shell;
+ private final LogicUICanvas ui;
+
+ public LogicUIStandaloneGUI(ViewModel model)
+ {
+ display = new Display();
+ shell = new Shell(display);
+ shell.setLayout(new FillLayout());
+ ui = new LogicUICanvas(shell, SWT.NONE, model);
+
+ ZoomableCanvasUserInput userInput = new ZoomableCanvasUserInput(ui);
+ userInput.buttonDrag = 3;
+ userInput.buttonZoom = 2;
+ userInput.enableUserInput();
+ new ZoomableCanvasOverlay(ui, null).enableScale();
+
+ // TODO don't do this here
+ LogicModelParameters params = new LogicModelParameters();
+ params.gateProcessTime = 50;
+ params.wireTravelTime = 10;
+// timeline = ViewLogicModelAdapter.convert(model, params);
+ }
+
+ public LogicUICanvas getLogicUICanvas()
+ {
+ return ui;
+ }
+
+ /**
+ * Opens the UI shell. Returns when the shell is closed.
+ */
+ public void run()
+ {
+ shell.open();
+ while (!shell.isDisposed())
+ if (!display.readAndDispatch())
+ display.sleep();
+ }
+}
\ No newline at end of file
--- /dev/null
+package mograsim.logic.ui;
+
+import java.util.function.Consumer;
+
+import mograsim.logic.core.timeline.Timeline;
+import mograsim.logic.ui.model.ViewModel;
+import mograsim.logic.ui.modeladapter.LogicModelParameters;
+import mograsim.logic.ui.modeladapter.ViewLogicModelAdapter;
+
+public class SimpleLogicUIStandalone
+{
+ public static void executeVisualisation(Consumer<ViewModel> setupViewModel)
+ {
+ // setup view model
+ ViewModel viewModel = new ViewModel();
+ setupViewModel.accept(viewModel);
+
+ // convert to logic model
+ LogicModelParameters params = new LogicModelParameters();
+ params.gateProcessTime = 50;
+ params.wireTravelTime = 10;
+ Timeline timeline = ViewLogicModelAdapter.convert(viewModel, params);
+
+ // initialize UI and executer
+ LogicUIStandaloneGUI ui = new LogicUIStandaloneGUI(viewModel);
+ LogicExecuter exec = new LogicExecuter(timeline);
+
+ // run it
+ exec.startLiveExecution();
+ ui.run();
+ exec.stopLiveExecution();
+ }
+
+ private SimpleLogicUIStandalone()
+ {
+ throw new UnsupportedOperationException("No SimpleLogicUIStandalone instances");
+ }
+}
\ No newline at end of file
--- /dev/null
+package mograsim.logic.ui.examples;
+
+import mograsim.logic.ui.SimpleLogicUIStandalone;
+import mograsim.logic.ui.model.ViewModel;
+import mograsim.logic.ui.model.components.GUIManualSwitch;
+import mograsim.logic.ui.model.components.GUINotGate;
+import mograsim.logic.ui.model.components.GUIOrGate;
+import mograsim.logic.ui.model.wires.GUIWire;
+import mograsim.logic.ui.model.wires.WireCrossPoint;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
+
+public class RSLatchExample
+{
+ public static void main(String[] args)
+ {
+ SimpleLogicUIStandalone.executeVisualisation(RSLatchExample::createRSLatchExample);
+ }
+
+ @SuppressWarnings("unused") // for GUIWires being created
+ public static void createRSLatchExample(ViewModel model)
+ {
+ GUIManualSwitch rIn = new GUIManualSwitch(model);
+ rIn.moveTo(100, 100);
+ GUIManualSwitch sIn = new GUIManualSwitch(model);
+ sIn.moveTo(100, 200);
+
+ GUIOrGate or1 = new GUIOrGate(model, 1);
+ or1.moveTo(160, 102.5);
+ new GUIWire(model, rIn.getOutputPin(), or1.getInputPins().get(0));
+
+ GUIOrGate or2 = new GUIOrGate(model, 1);
+ or2.moveTo(160, 192.5);
+ new GUIWire(model, sIn.getOutputPin(), or2.getInputPins().get(1));
+
+ GUINotGate not1 = new GUINotGate(model, 1);
+ not1.moveTo(200, 107.5);
+ new GUIWire(model, or1.getOutputPin(), not1.getInputPins().get(0));
+
+ GUINotGate not2 = new GUINotGate(model, 1);
+ not2.moveTo(200, 197.5);
+ new GUIWire(model, or2.getOutputPin(), not2.getInputPins().get(0));
+
+ WireCrossPoint p1 = new WireCrossPoint(model, 1);
+ p1.moveTo(250, 112.5);
+ new GUIWire(model, not1.getOutputPin(), p1.getPin());
+ new GUIWire(model, p1.getPin(), or2.getInputPins().get(0), new Point(250, 130), new Point(140, 185), new Point(140, 197.5));
+
+ WireCrossPoint p2 = new WireCrossPoint(model, 1);
+ p2.moveTo(250, 202.5);
+ new GUIWire(model, not2.getOutputPin(), p2.getPin());
+ new GUIWire(model, p2.getPin(), or1.getInputPins().get(1), new Point(250, 185), new Point(140, 130), new Point(140, 117.5));
+
+ WireCrossPoint o1 = new WireCrossPoint(model, 1);
+ o1.moveTo(270, 112.5);
+ new GUIWire(model, p1.getPin(), o1.getPin());
+
+ WireCrossPoint o2 = new WireCrossPoint(model, 1);
+ o2.moveTo(270, 202.5);
+ new GUIWire(model, p2.getPin(), o2.getPin());
+ }
+}
\ No newline at end of file
--- /dev/null
+package mograsim.logic.ui.model;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Consumer;
+
+import mograsim.logic.ui.model.components.GUIComponent;
+import mograsim.logic.ui.model.wires.GUIWire;
+
+public class ViewModel
+{
+ private final List<GUIComponent> components;
+ private final List<GUIComponent> componentsUnmodifiable;
+ private final List<GUIWire> wires;
+ private final List<GUIWire> wiresUnmodifiable;
+
+ private final List<Consumer<? super GUIComponent>> componentAddedListeners;
+ private final List<Consumer<? super GUIComponent>> componentRemovedListeners;
+ private final List<Consumer<? super GUIWire>> wireAddedListeners;
+ private final List<Consumer<? super GUIWire>> wireRemovedListeners;
+
+ public ViewModel()
+ {
+ components = new ArrayList<>();
+ componentsUnmodifiable = Collections.unmodifiableList(components);
+ wires = new ArrayList<>();
+ wiresUnmodifiable = Collections.unmodifiableList(wires);
+
+ componentAddedListeners = new ArrayList<>();
+ componentRemovedListeners = new ArrayList<>();
+ wireAddedListeners = new ArrayList<>();
+ wireRemovedListeners = new ArrayList<>();
+ }
+
+ /**
+ * Adds the given component to the list of components and calls all componentAddedListeners. Don't call this method from application
+ * code as it is automatically called in GUIComponent::new.
+ */
+ public void componentCreated(GUIComponent component)
+ {
+ if (components.contains(component))
+ throw new IllegalStateException("Don't add the same component twice!");
+ components.add(component);
+ callComponentAddedListeners(component);
+ }
+
+ /**
+ * Removes the given component from the list of components and calls all componentRemovedListeners. Don't call this method from
+ * application code as it is automatically called in GUIComponent::destroy.
+ */
+ public void componentDestroyed(GUIComponent component)
+ {
+ if (!components.contains(component))
+ throw new IllegalStateException("Don't remove the same component twice!");
+ components.remove(component);
+ callComponentRemovedListeners(component);
+ }
+
+ /**
+ * Adds the given component to the list of components and calls all componentAddedListeners. Don't call this method from application
+ * code as it is automatically called in GUIComponent::new.
+ */
+ public void wireCreated(GUIWire wire)
+ {
+ if (wires.contains(wire))
+ throw new IllegalStateException("Don't add the same wire twice!");
+ wires.add(wire);
+ callWireAddedListeners(wire);
+ }
+
+ /**
+ * Removes the given component from the list of components and calls all componentRemovedListeners. Don't call this method from
+ * application code as it is automatically called in GUIComponent::destroy.
+ */
+ public void wireDestroyed(GUIWire wire)
+ {
+ if (!wires.contains(wire))
+ throw new IllegalStateException("Don't remove the same wire twice!");
+ wires.remove(wire);
+ callWireRemovedListeners(wire);
+ }
+
+ public List<GUIComponent> getComponents()
+ {
+ return componentsUnmodifiable;
+ }
+
+ public List<GUIWire> getWires()
+ {
+ return wiresUnmodifiable;
+ }
+
+ // @formatter:off
+ public void addComponentAddedListener (Consumer<? super GUIComponent> listener){componentAddedListeners .add (listener);}
+ public void addComponentRemovedListener (Consumer<? super GUIComponent> listener){componentRemovedListeners.add (listener);}
+ public void addWireAddedListener (Consumer<? super GUIWire > listener){wireAddedListeners .add (listener);}
+ public void addWireRemovedListener (Consumer<? super GUIWire > listener){wireRemovedListeners .add (listener);}
+
+ public void removeComponentAddedListener (Consumer<? super GUIComponent> listener){componentAddedListeners .remove(listener);}
+ public void removeComponentRemovedListener(Consumer<? super GUIComponent> listener){componentRemovedListeners.remove(listener);}
+ public void removeWireAddedListener (Consumer<? super GUIWire > listener){wireAddedListeners .remove(listener);}
+ public void removeWireRemovedListener (Consumer<? super GUIWire > listener){wireRemovedListeners .remove(listener);}
+
+ private void callComponentAddedListeners (GUIComponent c) {componentAddedListeners .forEach(l -> l.accept(c));}
+ private void callComponentRemovedListeners(GUIComponent c) {componentRemovedListeners.forEach(l -> l.accept(c));}
+ private void callWireAddedListeners (GUIWire w ) {wireAddedListeners .forEach(l -> l.accept(w));}
+ private void callWireRemovedListeners (GUIWire w ) {wireRemovedListeners .forEach(l -> l.accept(w));}
+ // @formatter:on
+}
\ No newline at end of file
--- /dev/null
+package mograsim.logic.ui.model.components;
+
+import mograsim.logic.ui.model.ViewModel;
+
+public class GUIAndGate extends SimpleRectangularGUIGate
+{
+ public GUIAndGate(ViewModel model, int logicWidth)
+ {
+ super(model, logicWidth, "&", false);
+ setInputCount(2);// TODO make variable
+ }
+}
\ No newline at end of file
--- /dev/null
+package mograsim.logic.ui.model.components;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Consumer;
+
+import mograsim.logic.ui.model.ViewModel;
+import mograsim.logic.ui.model.wires.Pin;
+import net.haspamelodica.swt.helper.gcs.GeneralGC;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
+
+public abstract class GUIComponent
+{
+ protected final ViewModel model;
+ private final Rectangle bounds;
+ private final List<Pin> pins;
+ protected final List<Pin> pinsUnmodifiable;
+
+ private final List<Consumer<? super GUIComponent>> componentLookChangedListeners;
+ private final List<Consumer<? super GUIComponent>> componentMovedListeners;
+ private final List<Consumer<? super Pin>> pinAddedListeners;
+ private final List<Consumer<? super Pin>> pinRemovedListeners;
+
+ public GUIComponent(ViewModel model)
+ {
+ this.model = model;
+ this.bounds = new Rectangle(0, 0, 0, 0);
+ this.pins = new ArrayList<>();
+ this.pinsUnmodifiable = Collections.unmodifiableList(pins);
+
+ this.componentLookChangedListeners = new ArrayList<>();
+ this.componentMovedListeners = new ArrayList<>();
+ this.pinAddedListeners = new ArrayList<>();
+ this.pinRemovedListeners = new ArrayList<>();
+
+ model.componentCreated(this);
+ }
+
+ public void destroy()
+ {
+ pins.forEach(p -> pinRemovedListeners.forEach(l -> l.accept(p)));
+ model.componentDestroyed(this);
+ }
+
+ public void moveTo(double x, double y)
+ {
+ bounds.x = x;
+ bounds.y = y;
+ callComponentMovedListeners();
+ }
+
+ /**
+ * Returns the bounds of this component. Used for calculating which component is clicked.
+ */
+ public Rectangle getBounds()
+ {
+ return new Rectangle(bounds.x, bounds.y, bounds.width, bounds.height);
+ }
+
+ /**
+ * Called when this component is clicked. Absolute coordinates of the click are given. Returns true if this component consumed this
+ * click.
+ */
+ @SuppressWarnings({ "static-method", "unused" }) // this method is inteded to be overridden
+ public boolean clicked(double x, double y)
+ {
+ return false;
+ }
+
+ /**
+ * Returns a list of pins of this component.
+ */
+ public List<Pin> getPins()
+ {
+ return pinsUnmodifiable;
+ }
+
+ // @formatter:off
+ public void addComponentLookChangedListener (Consumer<? super GUIComponent> listener) {componentLookChangedListeners.add (listener);}
+ public void addComponentMovedListener (Consumer<? super GUIComponent> listener) {componentMovedListeners .add (listener);}
+ public void addPinAddedListener (Consumer<? super Pin > listener) {pinAddedListeners .add (listener);}
+ public void addPinRemovedListener (Consumer<? super Pin > listener) {pinRemovedListeners .add (listener);}
+
+ public void removeComponentLookChangedListener(Consumer<? super GUIComponent> listener) {componentLookChangedListeners.remove(listener);}
+ public void removeComponentMovedListener (Consumer<? super GUIComponent> listener) {componentMovedListeners .remove(listener);}
+ public void removePinAddedListener (Consumer<? super Pin > listener) {pinAddedListeners .remove(listener);}
+ public void removePinRemovedListener (Consumer<? super Pin > listener) {pinRemovedListeners .remove(listener);}
+
+ protected void callComponentLookChangedListeners( ) {componentLookChangedListeners.forEach(l -> l.accept(this));}
+ private void callComponentMovedListeners ( ) {componentMovedListeners .forEach(l -> l.accept(this));}
+ private void callPinAddedListeners (Pin p) {pinAddedListeners .forEach(l -> l.accept(p ));}
+ private void callPinRemovedListeners (Pin p) {pinRemovedListeners .forEach(l -> l.accept(p ));}
+ // @form atter:on
+
+ /**
+ * Render this component to the given gc.
+ */
+ public abstract void render(GeneralGC gc, Rectangle visibleRegion);
+
+ protected void setSize(double width, double height)
+ {
+ bounds.width = width;
+ bounds.height = height;
+ callComponentLookChangedListeners();
+ }
+
+ protected void addPin(Pin pin)
+ {
+ pins.add(pin);
+ callPinAddedListeners(pin);
+ }
+
+ protected void removePin(Pin pin)
+ {
+ pins.remove(pin);
+ callPinRemovedListeners(pin);
+ }
+}
\ No newline at end of file
--- /dev/null
+package mograsim.logic.ui.model.components;
+
+import mograsim.logic.core.components.ManualSwitch;
+import mograsim.logic.core.types.BitVectorFormatter;
+import mograsim.logic.core.wires.Wire.ReadEnd;
+import mograsim.logic.ui.model.ViewModel;
+import mograsim.logic.ui.model.wires.Pin;
+import net.haspamelodica.swt.helper.gcs.GeneralGC;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Font;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
+
+public class GUIManualSwitch extends GUIComponent
+{
+ private static final double width = 20;
+ private static final double height = 15;
+ private static final double fontHeight = 5;
+
+ private final Pin outputPin;
+
+ private ManualSwitch logicSwitch;
+ private ReadEnd end;
+
+ public GUIManualSwitch(ViewModel model)
+ {
+ super(model);
+ setSize(width, height);
+ addPin(this.outputPin = new Pin(this, 1, width, height / 2));
+ }
+
+ @Override
+ public void render(GeneralGC gc, Rectangle visibleRegion)
+ {
+ double posX = getBounds().x;
+ double posY = getBounds().y;
+
+ gc.drawRectangle(posX, posY, width, height);
+ String label = BitVectorFormatter.formatValueAsString(end);
+ Font oldFont = gc.getFont();
+ Font labelFont = new Font(oldFont.getName(), fontHeight, oldFont.getStyle());
+ gc.setFont(labelFont);
+ Point textExtent = gc.textExtent(label);
+ gc.drawText(label, posX + (width - textExtent.x) / 2, posY + (height - textExtent.y) / 2, true);
+ gc.setFont(oldFont);
+ }
+
+ public void setLogicModelBinding(ManualSwitch logicSwitch, ReadEnd end)
+ {
+ this.logicSwitch = logicSwitch;
+ this.end = end;
+ // TODO when ManualSwitch supports it, add listeners
+ end.addObserver((i, o) -> callComponentLookChangedListeners());
+ }
+
+ @Override
+ public boolean clicked(double x, double y)
+ {
+ if (logicSwitch != null)
+ logicSwitch.toggle();
+ return true;
+ }
+
+ public Pin getOutputPin()
+ {
+ return outputPin;
+ }
+}
\ No newline at end of file
--- /dev/null
+package mograsim.logic.ui.model.components;
+
+import mograsim.logic.ui.model.ViewModel;
+
+public class GUINotGate extends SimpleRectangularGUIGate
+{
+ public GUINotGate(ViewModel model, int logicWidth)
+ {
+ super(model, logicWidth, "1", true);
+ setInputCount(1);
+ }
+}
\ No newline at end of file
--- /dev/null
+package mograsim.logic.ui.model.components;
+
+import mograsim.logic.ui.model.ViewModel;
+
+public class GUIOrGate extends SimpleRectangularGUIGate
+{
+ public GUIOrGate(ViewModel model, int logicWidth)
+ {
+ super(model, logicWidth, "\u22651", false);// ">=1"
+ setInputCount(2);
+ }
+}
\ No newline at end of file
--- /dev/null
+package mograsim.logic.ui.model.components;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import mograsim.logic.ui.model.ViewModel;
+import mograsim.logic.ui.model.wires.MovablePin;
+import mograsim.logic.ui.model.wires.Pin;
+import net.haspamelodica.swt.helper.gcs.GeneralGC;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Font;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
+
+public class SimpleRectangularGUIGate extends GUIComponent
+{
+ private static final double width = 20;
+ private static final double pinDistance = 10;
+ private static final double fontHeight = 5;
+ private static final double invertedCircleDiam = 3.5;
+
+ private final String label;
+ protected final int logicWidth;
+ private final boolean isInverted;
+ private final double rectWidth;
+
+ private MovablePin outputPin;
+ private final List<Pin> inputPins;
+ private final List<Pin> inputPinsUnmodifiable;
+
+ protected SimpleRectangularGUIGate(ViewModel model, int logicWidth, String label, boolean isInverted)
+ {
+ super(model);
+ this.label = label;
+ this.logicWidth = logicWidth;
+ this.isInverted = isInverted;
+ this.rectWidth = width - (isInverted ? invertedCircleDiam : 0);
+ this.outputPin = new MovablePin(this, logicWidth, width, 0);
+ addPin(outputPin);
+ this.inputPins = new ArrayList<>();
+ this.inputPinsUnmodifiable = Collections.unmodifiableList(inputPins);
+ setInputCount(1);
+ }
+
+ protected void setInputCount(int inputCount)
+ {
+ int oldInputCount = inputPins.size();
+ setSize(width, inputCount * pinDistance);
+ if (oldInputCount > inputCount)
+ while (inputPins.size() > inputCount)
+ removePin(inputPins.get(inputCount));
+ else if (oldInputCount < inputCount)
+ for (int i = oldInputCount; i < inputCount; i++)
+ {
+ Pin pin = new Pin(this, logicWidth, 0, pinDistance / 2 + i * pinDistance);
+ inputPins.add(pin);
+ addPin(pin);
+ }
+ outputPin.setRelPos(width, inputCount * pinDistance / 2);
+ }
+
+ public Pin getOutputPin()
+ {
+ return outputPin;
+ }
+
+ public List<Pin> getInputPins()
+ {
+ return inputPinsUnmodifiable;
+ }
+
+ @Override
+ public void render(GeneralGC gc, Rectangle visibleRegion)
+ {
+ double posX = getBounds().x;
+ double posY = getBounds().y;
+
+ double height = inputPins.size() * pinDistance;
+ gc.drawRectangle(posX, posY, rectWidth, height);
+ Font oldFont = gc.getFont();
+ Font labelFont = new Font(oldFont.getName(), fontHeight, oldFont.getStyle());
+ gc.setFont(labelFont);
+ Point textExtent = gc.textExtent(label);
+ gc.drawText(label, posX + (rectWidth - textExtent.x) / 2, posY + (height - textExtent.y) / 2, true);
+ gc.setFont(oldFont);
+ if (isInverted)
+ gc.drawOval(posX + rectWidth, posY + (height - invertedCircleDiam) / 2, invertedCircleDiam, invertedCircleDiam);
+ }
+}
\ No newline at end of file
--- /dev/null
+package mograsim.logic.ui.model.wires;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+import mograsim.logic.core.types.BitVectorFormatter;
+import mograsim.logic.core.wires.Wire.ReadEnd;
+import mograsim.logic.ui.ColorHelper;
+import mograsim.logic.ui.model.ViewModel;
+import net.haspamelodica.swt.helper.gcs.GeneralGC;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
+
+public class GUIWire
+{
+ private final ViewModel model;
+ public final int logicWidth;
+ private Pin pin1;
+ private Pin pin2;
+ private double[] path;
+
+ private final List<Consumer<? super GUIWire>> wireLookChangedListeners;
+
+ private ReadEnd end;
+
+ public GUIWire(ViewModel model, Pin pin1, Pin pin2, Point... path)
+ {
+ this.model = model;
+ this.logicWidth = pin1.logicWidth;
+ if (pin2.logicWidth != pin1.logicWidth)
+ throw new IllegalArgumentException("Can't connect pins of different logic width");
+ this.path = new double[path.length * 2 + 4];
+ for (int srcI = 0, dstI = 2; srcI < path.length; srcI++, dstI += 2)
+ {
+ this.path[dstI + 0] = path[srcI].x;
+ this.path[dstI + 1] = path[srcI].y;
+ }
+
+ this.pin1 = pin1;
+ this.pin2 = pin2;
+
+ wireLookChangedListeners = new ArrayList<>();
+
+ pin1.addPinMovedListener(p -> pin1Moved());
+ pin2.addPinMovedListener(p -> pin2Moved());
+ pin1Moved();
+ pin2Moved();
+
+ model.wireCreated(this);
+ }
+
+ private void pin1Moved()
+ {
+ Point pos = pin1.getPos();
+ this.path[0] = pos.x;
+ this.path[1] = pos.y;
+ }
+
+ private void pin2Moved()
+ {
+ Point pos = pin2.getPos();
+ this.path[this.path.length - 2] = pos.x;
+ this.path[this.path.length - 1] = pos.y;
+ }
+
+ public void destroy()
+ {
+ model.wireDestroyed(this);
+ }
+
+ public void render(GeneralGC gc)
+ {
+ ColorHelper.executeWithDifferentForeground(gc, BitVectorFormatter.formatAsColor(end), () -> gc.drawPolyline(path));
+ }
+
+ public void setLogicModelBinding(ReadEnd end)
+ {
+ this.end = end;
+ end.addObserver((i, o) -> callWireLookChangedListeners());
+ }
+
+ public Pin getPin1()
+ {
+ return pin1;
+ }
+
+ public Pin getPin2()
+ {
+ return pin2;
+ }
+
+ // @formatter:off
+ public void addWireLookChangedListener (Consumer<? super GUIWire> listener) {wireLookChangedListeners.add (listener);}
+
+ public void removeWireLookChangedListener(Consumer<? super GUIWire> listener) {wireLookChangedListeners.remove(listener);}
+
+ private void callWireLookChangedListeners() {wireLookChangedListeners.forEach(l -> l.accept(this));}
+ // @formatter:on
+
+}
\ No newline at end of file
--- /dev/null
+package mograsim.logic.ui.model.wires;
+
+import mograsim.logic.ui.model.components.GUIComponent;
+
+public class MovablePin extends Pin
+{
+ public MovablePin(GUIComponent component, int logicWidth, double relX, double relY)
+ {
+ super(component, logicWidth, relX, relY);
+ }
+
+ @Override
+ public void setRelPos(double relX, double relY)
+ {
+ super.setRelPos(relX, relY);
+ }
+}
\ No newline at end of file
--- /dev/null
+package mograsim.logic.ui.model.wires;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+import mograsim.logic.ui.model.components.GUIComponent;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Point;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
+
+public class Pin
+{
+ public final GUIComponent component;
+ public final int logicWidth;
+
+ protected double relX;
+ protected double relY;
+
+ private final List<Consumer<? super Pin>> pinMovedListeners;
+
+ public Pin(GUIComponent component, int logicWidth, double relX, double relY)
+ {
+ this.component = component;
+ this.logicWidth = logicWidth;
+ this.relX = relX;
+ this.relY = relY;
+
+ this.pinMovedListeners = new ArrayList<>();
+
+ component.addComponentMovedListener(c -> callPinMovedListeners());
+ }
+
+ public double getRelX()
+ {
+ return relX;
+ }
+
+ public double getRelY()
+ {
+ return relY;
+ }
+
+ public Point getRelPos()
+ {
+ return new Point(relX, relY);
+ }
+
+ public Point getPos()
+ {
+ Rectangle componentBounds = component.getBounds();
+ return new Point(relX + componentBounds.x, relY + componentBounds.y);
+ }
+
+ // @formatter:off
+ public void addPinMovedListener (Consumer<? super Pin> listener){pinMovedListeners.add (listener);}
+
+ public void removePinMovedListener(Consumer<? super Pin> listener){pinMovedListeners.remove(listener);}
+
+ private void callPinMovedListeners() {pinMovedListeners.forEach(l -> l.accept(this));}
+ // @formatter:on
+
+ protected void setRelPos(double relX, double relY)
+ {
+ this.relX = relX;
+ this.relY = relY;
+ callPinMovedListeners();
+ }
+}
\ No newline at end of file
--- /dev/null
+package mograsim.logic.ui.model.wires;
+
+import mograsim.logic.core.types.BitVectorFormatter;
+import mograsim.logic.core.wires.Wire.ReadEnd;
+import mograsim.logic.ui.ColorHelper;
+import mograsim.logic.ui.model.ViewModel;
+import mograsim.logic.ui.model.components.GUIComponent;
+import net.haspamelodica.swt.helper.gcs.GeneralGC;
+import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle;
+
+public class WireCrossPoint extends GUIComponent
+{
+ private final Pin pin;
+
+ private ReadEnd end;
+ private final int logicWidth;
+
+ public WireCrossPoint(ViewModel model, int logicWidth)
+ {
+ super(model);
+ this.logicWidth = logicWidth;
+ setSize(0, 0);
+ addPin(this.pin = new Pin(this, logicWidth, 0, 0));
+ }
+
+ @Override
+ public void render(GeneralGC gc, Rectangle visibleRegion)
+ {
+ Rectangle bounds = getBounds();
+ ColorHelper.executeWithDifferentBackground(gc, BitVectorFormatter.formatAsColor(end),
+ () -> gc.fillOval(bounds.x - 1, bounds.y - 1, 2, 2));
+ }
+
+ public void setLogicModelBinding(ReadEnd end)
+ {
+ this.end = end;
+ end.addObserver((i, o) -> callComponentLookChangedListeners());
+ }
+
+ public int getLogicWidth()
+ {
+ return logicWidth;
+ }
+
+ public Pin getPin()
+ {
+ return pin;
+ }
+}
\ No newline at end of file
--- /dev/null
+package mograsim.logic.ui.modeladapter;
+
+public class LogicModelParameters
+{
+ public int wireTravelTime;
+ public int gateProcessTime;
+}
\ No newline at end of file
--- /dev/null
+package mograsim.logic.ui.modeladapter;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import mograsim.logic.core.components.Component;
+import mograsim.logic.core.components.gates.AndGate;
+import mograsim.logic.core.components.gates.NotGate;
+import mograsim.logic.core.components.gates.OrGate;
+import mograsim.logic.core.timeline.Timeline;
+import mograsim.logic.core.wires.Wire;
+import mograsim.logic.core.wires.Wire.ReadEnd;
+import mograsim.logic.ui.model.ViewModel;
+import mograsim.logic.ui.model.components.GUIAndGate;
+import mograsim.logic.ui.model.components.GUIComponent;
+import mograsim.logic.ui.model.components.GUINotGate;
+import mograsim.logic.ui.model.components.GUIOrGate;
+import mograsim.logic.ui.model.wires.GUIWire;
+import mograsim.logic.ui.model.wires.Pin;
+import mograsim.logic.ui.model.wires.WireCrossPoint;
+import mograsim.logic.ui.modeladapter.componentadapters.ComponentAdapter;
+import mograsim.logic.ui.modeladapter.componentadapters.ManualSwitchAdapter;
+import mograsim.logic.ui.modeladapter.componentadapters.SimpleGateAdapter;
+
+public class ViewLogicModelAdapter
+{
+ private final static Map<Class<? extends GUIComponent>, ComponentAdapter<? extends GUIComponent>> componentAdapters;
+ static
+ {
+ Set<ComponentAdapter<? extends GUIComponent>> componentAdaptersModifiable = new HashSet<>();
+ componentAdaptersModifiable.add(new SimpleGateAdapter<>(GUIOrGate.class, OrGate::new));
+ componentAdaptersModifiable.add(new SimpleGateAdapter<>(GUIAndGate.class, AndGate::new));
+ componentAdaptersModifiable.add(new SimpleGateAdapter<>(GUINotGate.class, (t, p, o, i) -> new NotGate(t, p, i[0], o)));
+ componentAdaptersModifiable.add(new ManualSwitchAdapter());
+ // TODO list all "primitive" adapters here
+ componentAdapters = Collections.unmodifiableMap(
+ componentAdaptersModifiable.stream().collect(Collectors.toMap(ComponentAdapter::getSupportedClass, Function.identity())));
+ }
+
+ public static Timeline convert(ViewModel viewModel, LogicModelParameters params)
+ {
+ // TODO replace Timeline with LogicModel as soon as it exists
+ Timeline timeline = new Timeline(10);
+
+ Map<Pin, Wire> logicWiresPerPin = convertWires(
+ viewModel.getComponents().stream().flatMap(component -> component.getPins().stream()).collect(Collectors.toSet()),
+ viewModel.getWires(), params, timeline);
+ Map<Pin, Wire> logicWiresPerPinUnmodifiable = Collections.unmodifiableMap(logicWiresPerPin);
+
+ Map<GUIComponent, Component> oneToOneComponents = new HashMap<>();
+ for (GUIComponent guiComp : viewModel.getComponents())
+ {
+ if (!(guiComp instanceof WireCrossPoint))
+ oneToOneComponents.put(guiComp, createAndLinkComponent(timeline, params, guiComp, logicWiresPerPinUnmodifiable,
+ componentAdapters.get(guiComp.getClass())));
+ else
+ {
+ WireCrossPoint guiCompCasted = (WireCrossPoint) guiComp;
+ guiCompCasted.setLogicModelBinding(logicWiresPerPin.get(guiCompCasted.getPin()).createReadOnlyEnd());
+ }
+ }
+
+ // TODO handle complex components
+
+ List<Component> logicComponents = new ArrayList<>();
+ logicComponents.addAll(oneToOneComponents.values());
+
+ return timeline;
+ }
+
+ private static Map<Pin, Wire> convertWires(Set<Pin> allPins, List<GUIWire> wires, LogicModelParameters params, Timeline timeline)
+ {
+ Map<Pin, Set<Pin>> connectedPinGroups = getConnectedPinGroups(allPins, wires);
+ Map<Pin, Wire> logicWiresPerPin = createLogicWires(params, timeline, connectedPinGroups);
+ setGUIWiresLogicModelBinding(wires, logicWiresPerPin);
+ return logicWiresPerPin;
+ }
+
+ private static Map<Pin, Wire> createLogicWires(LogicModelParameters params, Timeline timeline, Map<Pin, Set<Pin>> connectedPinGroups)
+ {
+ Map<Pin, Wire> logicWiresPerPin = new HashMap<>();
+ Map<Set<Pin>, Wire> logicWiresPerPinGroup = new HashMap<>();
+ for (Entry<Pin, Set<Pin>> e : connectedPinGroups.entrySet())
+ logicWiresPerPin.put(e.getKey(), logicWiresPerPinGroup.computeIfAbsent(e.getValue(),
+ set -> new Wire(timeline, e.getKey().logicWidth, params.wireTravelTime)));
+ return logicWiresPerPin;
+ }
+
+ private static void setGUIWiresLogicModelBinding(List<GUIWire> wires, Map<Pin, Wire> logicWiresPerPin)
+ {
+ Map<Wire, ReadEnd> guiWireSharedReadEnd = logicWiresPerPin.values().stream().distinct()
+ .collect(Collectors.toMap(Function.identity(), Wire::createReadOnlyEnd));
+ for (GUIWire guiWire : wires)
+ guiWire.setLogicModelBinding(guiWireSharedReadEnd.get(logicWiresPerPin.get(guiWire.getPin1())));
+ }
+
+ private static Map<Pin, Set<Pin>> getConnectedPinGroups(Set<Pin> allPins, List<GUIWire> wires)
+ {
+ Map<Pin, Set<Pin>> connectedPinsPerPin = new HashMap<>();
+
+ for (Pin p : allPins)
+ {
+ HashSet<Pin> connectedPins = new HashSet<>();
+ connectedPins.add(p);
+ connectedPinsPerPin.put(p, connectedPins);
+ }
+
+ wires.forEach(wire ->
+ {
+ Pin pin1 = wire.getPin1();
+ Pin pin2 = wire.getPin2();
+
+ Set<Pin> pin1ConnectedPins = connectedPinsPerPin.get(pin1);
+ Set<Pin> pin2ConnectedPins = connectedPinsPerPin.get(pin2);
+
+ pin1ConnectedPins.addAll(pin2ConnectedPins);
+ pin1ConnectedPins.add(pin1);
+ pin1ConnectedPins.add(pin2);
+
+ pin2ConnectedPins.forEach(pin -> connectedPinsPerPin.put(pin, pin1ConnectedPins));
+ });
+ return connectedPinsPerPin;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <G extends GUIComponent> Component createAndLinkComponent(Timeline timeline, LogicModelParameters params,
+ GUIComponent guiComponent, Map<Pin, Wire> logicWiresPerPin, ComponentAdapter<G> adapter)
+ {
+ if (adapter == null)
+ throw new IllegalArgumentException("Unknown component class: " + guiComponent.getClass());
+ return adapter.createAndLinkComponent(timeline, params, (G) guiComponent, logicWiresPerPin);
+ }
+
+ private ViewLogicModelAdapter()
+ {
+ throw new UnsupportedOperationException("No ViewLogicModelConverter instances");
+ }
+}
\ No newline at end of file
--- /dev/null
+package mograsim.logic.ui.modeladapter.componentadapters;
+
+import java.util.Map;
+
+import mograsim.logic.core.components.Component;
+import mograsim.logic.core.timeline.Timeline;
+import mograsim.logic.core.wires.Wire;
+import mograsim.logic.ui.model.components.GUIComponent;
+import mograsim.logic.ui.model.wires.Pin;
+import mograsim.logic.ui.modeladapter.LogicModelParameters;
+
+public interface ComponentAdapter<G extends GUIComponent>
+{
+ public Class<G> getSupportedClass();
+
+ public Component createAndLinkComponent(Timeline timeline, LogicModelParameters params, G guiComponent,
+ Map<Pin, Wire> logicWiresPerPin);
+}
\ No newline at end of file
--- /dev/null
+package mograsim.logic.ui.modeladapter.componentadapters;
+
+import java.util.Map;
+
+import mograsim.logic.core.components.Component;
+import mograsim.logic.core.components.ManualSwitch;
+import mograsim.logic.core.timeline.Timeline;
+import mograsim.logic.core.wires.Wire;
+import mograsim.logic.core.wires.Wire.ReadWriteEnd;
+import mograsim.logic.ui.model.components.GUIManualSwitch;
+import mograsim.logic.ui.model.wires.Pin;
+import mograsim.logic.ui.modeladapter.LogicModelParameters;
+
+public class ManualSwitchAdapter implements ComponentAdapter<GUIManualSwitch>
+{
+ @Override
+ public Class<GUIManualSwitch> getSupportedClass()
+ {
+ return GUIManualSwitch.class;
+ }
+
+ @Override
+ public Component createAndLinkComponent(Timeline timeline, LogicModelParameters params, GUIManualSwitch guiComponent,
+ Map<Pin, Wire> logicWiresPerPin)
+ {
+ ReadWriteEnd end = logicWiresPerPin.get(guiComponent.getOutputPin()).createReadWriteEnd();
+ ManualSwitch manualSwitch = new ManualSwitch(timeline, end);
+ guiComponent.setLogicModelBinding(manualSwitch, end);
+ return manualSwitch;
+ }
+}
\ No newline at end of file
--- /dev/null
+package mograsim.logic.ui.modeladapter.componentadapters;
+
+import java.util.List;
+import java.util.Map;
+
+import mograsim.logic.core.components.Component;
+import mograsim.logic.core.timeline.Timeline;
+import mograsim.logic.core.wires.Wire;
+import mograsim.logic.core.wires.Wire.ReadEnd;
+import mograsim.logic.core.wires.Wire.ReadWriteEnd;
+import mograsim.logic.ui.model.components.SimpleRectangularGUIGate;
+import mograsim.logic.ui.model.wires.Pin;
+import mograsim.logic.ui.modeladapter.LogicModelParameters;
+
+public class SimpleGateAdapter<G extends SimpleRectangularGUIGate> implements ComponentAdapter<G>
+{
+ private final Class<G> supportedClass;
+ private final ComponentConstructor constructor;
+
+ public SimpleGateAdapter(Class<G> supportedClass, ComponentConstructor constructor)
+ {
+ this.supportedClass = supportedClass;
+ this.constructor = constructor;
+ }
+
+ @Override
+ public Class<G> getSupportedClass()
+ {
+ return supportedClass;
+ }
+
+ @Override
+ public Component createAndLinkComponent(Timeline timeline, LogicModelParameters params, G guiComponent, Map<Pin, Wire> logicWiresPerPin)
+ {
+ ReadWriteEnd out = logicWiresPerPin.get(guiComponent.getOutputPin()).createReadWriteEnd();
+ List<Pin> inputPins = guiComponent.getInputPins();
+ ReadEnd[] ins = new ReadEnd[inputPins.size()];
+ for (int i = 0; i < inputPins.size(); i++)
+ ins[i] = logicWiresPerPin.get(inputPins.get(i)).createReadOnlyEnd();
+ return constructor.newComponent(timeline, params.gateProcessTime, out, ins);
+ }
+
+ public static interface ComponentConstructor
+ {
+ public Component newComponent(Timeline timeline, int processTime, ReadWriteEnd out, ReadEnd[] ins);
+ }
+
+}
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
- <name>SampleERCP</name>
+ <name>mograsim.rcp</name>
<comment></comment>
<projects>
- <project>LogicUI</project>
+ <project>mograsim.rcp</project>
</projects>
<buildSpec>
<buildCommand>
eclipse.preferences.version=1
editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
-formatter_profile=_ERA-MI
+formatter_profile=_MoGraSim
formatter_settings_version=16
org.eclipse.jdt.ui.text.custom_code_templates=
sp_cleanup.add_default_serial_version_id=true
<?xml version="1.0" encoding="UTF-8"?>
<application:Application xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:application="http://www.eclipse.org/ui/2010/UIModel/application" xmlns:basic="http://www.eclipse.org/ui/2010/UIModel/application/ui/basic" xmlns:menu="http://www.eclipse.org/ui/2010/UIModel/application/ui/menu" xmi:id="_6wlLcMgZEeSyMNYR5xypkQ" elementId="Sample.application" bindingContexts="_6wlLecgZEeSyMNYR5xypkQ">
- <children xsi:type="basic:TrimmedWindow" xmi:id="_6wlLccgZEeSyMNYR5xypkQ" elementId="Sample.window.main" label="Sample RCP4" iconURI="platform:/plugin/SampleERCP/icons/app/ico_16t.png" width="500" height="400">
+ <children xsi:type="basic:TrimmedWindow" xmi:id="_6wlLccgZEeSyMNYR5xypkQ" elementId="Sample.window.main" label="Sample RCP4" iconURI="platform:/plugin/mograsim.rcp/icons/app/ico_16t.png" width="500" height="400">
<children xsi:type="basic:PartSashContainer" xmi:id="_6wlLksgZEeSyMNYR5xypkQ" elementId="Sample.partsashcontainer.sample">
<children xsi:type="basic:PartStack" xmi:id="_6wlLk8gZEeSyMNYR5xypkQ" elementId="Sample.partstack.sample">
- <children xsi:type="basic:Part" xmi:id="_6wlLlMgZEeSyMNYR5xypkQ" elementId="Sample.part.sample" contributionURI="bundleclass://SampleERCP/sampleercp.parts.SamplePart" label="Sample Part" iconURI="platform:/plugin/SampleERCP/icons/home.png"/>
- <children xsi:type="basic:Part" xmi:id="_cldhcH1HEema06ZDPR6BSw" elementId="sampleercp.part.logicui" contributionURI="bundleclass://SampleERCP/sampleercp.parts.LogicUIPart" label="LogicUI part"/>
+ <children xsi:type="basic:Part" xmi:id="_6wlLlMgZEeSyMNYR5xypkQ" elementId="Sample.part.sample" contributionURI="bundleclass://mograsim.rcp/mograsim.rcp.parts.SamplePart" label="Sample Part" iconURI="platform:/plugin/mograsim.rcp/icons/home.png"/>
+ <children xsi:type="basic:Part" xmi:id="_cldhcH1HEema06ZDPR6BSw" elementId="mograsim.rcp.part.logicui" contributionURI="bundleclass://mograsim.rcp/mograsim.rcp.parts.LogicUIPart" label="LogicUI part"/>
</children>
<children xsi:type="basic:PartStack" xmi:id="_l2EzYG6wEemo__tDmTCqCQ" elementId="sample.partstack.0">
<children xsi:type="basic:Part" xmi:id="_mW1XEG6wEemo__tDmTCqCQ" elementId="sample.part.none" label="None" tooltip="Something"/>
</children>
<mainMenu xmi:id="_6wlLicgZEeSyMNYR5xypkQ" elementId="org.eclipse.ui.main.menu">
<children xsi:type="menu:Menu" xmi:id="_6wlLisgZEeSyMNYR5xypkQ" elementId="file" label="File">
- <children xsi:type="menu:HandledMenuItem" xmi:id="_6wlLi8gZEeSyMNYR5xypkQ" elementId="Sample.handleditem.file.open" label="Open" iconURI="platform:/plugin/SampleERCP/icons/open_in_app.png" command="_6wlLgMgZEeSyMNYR5xypkQ"/>
- <children xsi:type="menu:HandledMenuItem" xmi:id="_6wlLjMgZEeSyMNYR5xypkQ" elementId="Sample.handleditem.save" label="Save" iconURI="platform:/plugin/SampleERCP/icons/save_edit.png" command="_6wlLg8gZEeSyMNYR5xypkQ"/>
+ <children xsi:type="menu:HandledMenuItem" xmi:id="_6wlLi8gZEeSyMNYR5xypkQ" elementId="Sample.handleditem.file.open" label="Open" iconURI="platform:/plugin/mograsim.rcp/icons/open_in_app.png" command="_6wlLgMgZEeSyMNYR5xypkQ"/>
+ <children xsi:type="menu:HandledMenuItem" xmi:id="_6wlLjMgZEeSyMNYR5xypkQ" elementId="Sample.handleditem.save" label="Save" iconURI="platform:/plugin/mograsim.rcp/icons/save_edit.png" command="_6wlLg8gZEeSyMNYR5xypkQ"/>
<children xsi:type="menu:HandledMenuItem" xmi:id="_6wlLjcgZEeSyMNYR5xypkQ" elementId="Sample.handleditem.quit" label="Quit" command="_6wlLfMgZEeSyMNYR5xypkQ"/>
</children>
<children xsi:type="menu:Menu" xmi:id="_6wlLjsgZEeSyMNYR5xypkQ" elementId="help" label="Help">
</mainMenu>
<trimBars xmi:id="_6wlLlcgZEeSyMNYR5xypkQ" elementId="Sample.trimbar.top">
<children xsi:type="menu:ToolBar" xmi:id="_6wlLlsgZEeSyMNYR5xypkQ" elementId="org.eclipse.ui.main.toolbar">
- <children xsi:type="menu:HandledToolItem" xmi:id="_6wlLmMgZEeSyMNYR5xypkQ" elementId="Sample.handleditem.trimbar.top.save" iconURI="platform:/plugin/SampleERCP/icons/save_edit.png" command="_6wlLg8gZEeSyMNYR5xypkQ"/>
- <children xsi:type="menu:HandledToolItem" xmi:id="_6wlLl8gZEeSyMNYR5xypkQ" elementId="Sample.handleditem.trimbar.top.open" iconURI="platform:/plugin/SampleERCP/icons/open_in_app.png" command="_6wlLgMgZEeSyMNYR5xypkQ"/>
+ <children xsi:type="menu:HandledToolItem" xmi:id="_6wlLmMgZEeSyMNYR5xypkQ" elementId="Sample.handleditem.trimbar.top.save" iconURI="platform:/plugin/mograsim.rcp/icons/save_edit.png" command="_6wlLg8gZEeSyMNYR5xypkQ"/>
+ <children xsi:type="menu:HandledToolItem" xmi:id="_6wlLl8gZEeSyMNYR5xypkQ" elementId="Sample.handleditem.trimbar.top.open" iconURI="platform:/plugin/mograsim.rcp/icons/open_in_app.png" command="_6wlLgMgZEeSyMNYR5xypkQ"/>
</children>
</trimBars>
</children>
- <handlers xmi:id="_6wlLfcgZEeSyMNYR5xypkQ" elementId="sample.handler.quitCommand" contributionURI="bundleclass://SampleERCP/sampleercp.handlers.QuitHandler" command="_6wlLfMgZEeSyMNYR5xypkQ"/>
- <handlers xmi:id="_6wlLgcgZEeSyMNYR5xypkQ" elementId="sample.handler.openCommand" contributionURI="bundleclass://SampleERCP/sampleercp.handlers.OpenHandler" command="_6wlLgMgZEeSyMNYR5xypkQ"/>
- <handlers xmi:id="_6wlLhMgZEeSyMNYR5xypkQ" elementId="sample.handler.saveCommand" contributionURI="bundleclass://SampleERCP/sampleercp.handlers.SaveHandler" command="_6wlLg8gZEeSyMNYR5xypkQ"/>
- <handlers xmi:id="_6wlLh8gZEeSyMNYR5xypkQ" elementId="sample.handler.aboutCommand" contributionURI="bundleclass://SampleERCP/sampleercp.handlers.AboutHandler" command="_6wlLhsgZEeSyMNYR5xypkQ"/>
+ <handlers xmi:id="_6wlLfcgZEeSyMNYR5xypkQ" elementId="sample.handler.quitCommand" contributionURI="bundleclass://mograsim.rcp/mograsim.rcp.handlers.QuitHandler" command="_6wlLfMgZEeSyMNYR5xypkQ"/>
+ <handlers xmi:id="_6wlLgcgZEeSyMNYR5xypkQ" elementId="sample.handler.openCommand" contributionURI="bundleclass://mograsim.rcp/mograsim.rcp.handlers.OpenHandler" command="_6wlLgMgZEeSyMNYR5xypkQ"/>
+ <handlers xmi:id="_6wlLhMgZEeSyMNYR5xypkQ" elementId="sample.handler.saveCommand" contributionURI="bundleclass://mograsim.rcp/mograsim.rcp.handlers.SaveHandler" command="_6wlLg8gZEeSyMNYR5xypkQ"/>
+ <handlers xmi:id="_6wlLh8gZEeSyMNYR5xypkQ" elementId="sample.handler.aboutCommand" contributionURI="bundleclass://mograsim.rcp/mograsim.rcp.handlers.AboutHandler" command="_6wlLhsgZEeSyMNYR5xypkQ"/>
<bindingTables xmi:id="_6wlLfsgZEeSyMNYR5xypkQ" elementId="Sample.bindingtable" bindingContext="_6wlLecgZEeSyMNYR5xypkQ">
<bindings xmi:id="_6wlLf8gZEeSyMNYR5xypkQ" elementId="Sample.keybinding.m1q" keySequence="M1+Q" command="_6wlLfMgZEeSyMNYR5xypkQ"/>
<bindings xmi:id="_6wlLgsgZEeSyMNYR5xypkQ" elementId="Sample.keybinding.m1o" keySequence="M1+O" command="_6wlLgMgZEeSyMNYR5xypkQ"/>
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %Bundle-Name
-Bundle-SymbolicName: SampleERCP;singleton:=true
+Bundle-SymbolicName: mograsim.rcp;singleton:=true
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: %Bundle-Vendor
Require-Bundle: org.eclipse.core.runtime;bundle-version="3.15.200",
org.eclipse.e4.ui.di;bundle-version="1.2.500",
org.eclipse.ui.workbench;bundle-version="3.113.0",
javax.annotation;bundle-version="1.2.0",
- LogicUI,
- era.mi
+ mograsim.logic.ui
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Automatic-Module-Name: Sample
-Export-Package: sampleercp.handlers;uses:="org.eclipse.swt.widgets,org.eclipse.e4.ui.workbench,org.eclipse.e4.ui.workbench.modeling",
- sampleercp.parts;uses:="org.eclipse.swt.widgets",
- sampleercp.splashhandlers
+Export-Package: mograsim.rcp.handlers;uses:="org.eclipse.swt.widgets,org.eclipse.e4.ui.workbench,org.eclipse.e4.ui.workbench.modeling",
+ mograsim.rcp.parts;uses:="org.eclipse.swt.widgets",
+ mograsim.rcp.splashhandlers
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<?pde version="3.5"?>
-
-<product name="Simulator WIP" uid="sample" id="SampleERCP.product" application="org.eclipse.e4.ui.workbench.swt.E4Application" version="1.0.0.qualifier" useFeatures="false" includeLaunchers="true">
-
- <aboutInfo>
- <text>
- Example
- </text>
- </aboutInfo>
-
- <configIni use="default">
- </configIni>
-
- <launcherArgs>
- <programArgs>-clearPersistedState
- </programArgs>
- <vmArgsMac>-XstartOnFirstThread -Dorg.eclipse.swt.internal.carbon.smallFonts
- </vmArgsMac>
- </launcherArgs>
-
- <windowImages i16="/SampleERCP/icons/app/ico_16t.png" i32="/SampleERCP/icons/app/ico_32t.png" i48="/SampleERCP/icons/app/ico_48t.png" i64="/SampleERCP/icons/app/ico_64t.png" i128="/SampleERCP/icons/app/ico_128t.png" i256="/SampleERCP/icons/app/ico_256t.png"/>
-
- <splash
- handlerType="extensible" />
- <launcher name="Sample">
- <linux icon="/SampleERCP/icons/app/ico_256t.xpm"/>
- <macosx icon="/SampleERCP/icons/app/ico_256t.icns"/>
- <win useIco="true">
- <ico path="/SampleERCP/icons/app/ico_all.ico"/>
- <bmp/>
- </win>
- </launcher>
-
- <vm>
- <linux include="false">org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11</linux>
- <macos include="false">org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11</macos>
- <windows include="false">org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11</windows>
- </vm>
-
- <license>
- <url>https://www.eclipse.org/legal/epl-2.0/</url>
- <text>
- Copyright (c) 2019 Christian Femers, Daniel Kirschten and Fabian Stemmler
-
-This program and the accompanying materials are made
-available under the terms of the Eclipse Public License 2.0
-which is available at https://www.eclipse.org/legal/epl-2.0/
-
-This Source Code may also be made available under the following Secondary
-Licenses when the conditions for such availability set forth in the Eclipse
-Public License, v. 2.0 are satisfied: GNU General Public License, version 2
-with the GNU Classpath Exception which is
-available at https://www.gnu.org/software/classpath/license.html.
-
-SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
- </text>
- </license>
-
- <plugins>
- <plugin id="LogicUI"/>
- <plugin id="SWTBufferedCanvas"/>
- <plugin id="SWTObjectWrappers"/>
- <plugin id="SWTZoomableCanvas"/>
- <plugin id="SampleERCP"/>
- <plugin id="com.ibm.icu"/>
- <plugin id="era.mi"/>
- <plugin id="javax.annotation"/>
- <plugin id="javax.inject"/>
- <plugin id="javax.servlet"/>
- <plugin id="javax.xml"/>
- <plugin id="org.apache.batik.constants"/>
- <plugin id="org.apache.batik.css"/>
- <plugin id="org.apache.batik.i18n"/>
- <plugin id="org.apache.batik.util"/>
- <plugin id="org.apache.commons.io"/>
- <plugin id="org.apache.commons.jxpath"/>
- <plugin id="org.apache.commons.logging"/>
- <plugin id="org.apache.felix.gogo.runtime"/>
- <plugin id="org.apache.felix.scr"/>
- <plugin id="org.apache.log4j"/>
- <plugin id="org.apache.xmlgraphics"/>
- <plugin id="org.eclipse.ant.core"/>
- <plugin id="org.eclipse.core.commands"/>
- <plugin id="org.eclipse.core.contenttype"/>
- <plugin id="org.eclipse.core.databinding"/>
- <plugin id="org.eclipse.core.databinding.beans"/>
- <plugin id="org.eclipse.core.databinding.observable"/>
- <plugin id="org.eclipse.core.databinding.property"/>
- <plugin id="org.eclipse.core.expressions"/>
- <plugin id="org.eclipse.core.filesystem"/>
- <plugin id="org.eclipse.core.filesystem.win32.x86_64" fragment="true"/>
- <plugin id="org.eclipse.core.jobs"/>
- <plugin id="org.eclipse.core.resources"/>
- <plugin id="org.eclipse.core.resources.win32.x86_64" fragment="true"/>
- <plugin id="org.eclipse.core.runtime"/>
- <plugin id="org.eclipse.core.variables"/>
- <plugin id="org.eclipse.e4.core.commands"/>
- <plugin id="org.eclipse.e4.core.contexts"/>
- <plugin id="org.eclipse.e4.core.di"/>
- <plugin id="org.eclipse.e4.core.di.annotations"/>
- <plugin id="org.eclipse.e4.core.di.extensions"/>
- <plugin id="org.eclipse.e4.core.di.extensions.supplier"/>
- <plugin id="org.eclipse.e4.core.services"/>
- <plugin id="org.eclipse.e4.emf.xpath"/>
- <plugin id="org.eclipse.e4.ui.bindings"/>
- <plugin id="org.eclipse.e4.ui.css.core"/>
- <plugin id="org.eclipse.e4.ui.css.swt"/>
- <plugin id="org.eclipse.e4.ui.css.swt.theme"/>
- <plugin id="org.eclipse.e4.ui.di"/>
- <plugin id="org.eclipse.e4.ui.model.workbench"/>
- <plugin id="org.eclipse.e4.ui.services"/>
- <plugin id="org.eclipse.e4.ui.swt.gtk" fragment="true"/>
- <plugin id="org.eclipse.e4.ui.widgets"/>
- <plugin id="org.eclipse.e4.ui.workbench"/>
- <plugin id="org.eclipse.e4.ui.workbench.addons.swt"/>
- <plugin id="org.eclipse.e4.ui.workbench.renderers.swt"/>
- <plugin id="org.eclipse.e4.ui.workbench.renderers.swt.cocoa" fragment="true"/>
- <plugin id="org.eclipse.e4.ui.workbench.swt"/>
- <plugin id="org.eclipse.e4.ui.workbench3"/>
- <plugin id="org.eclipse.emf.common"/>
- <plugin id="org.eclipse.emf.databinding"/>
- <plugin id="org.eclipse.emf.ecore"/>
- <plugin id="org.eclipse.emf.ecore.change"/>
- <plugin id="org.eclipse.emf.ecore.xmi"/>
- <plugin id="org.eclipse.equinox.app"/>
- <plugin id="org.eclipse.equinox.bidi"/>
- <plugin id="org.eclipse.equinox.common"/>
- <plugin id="org.eclipse.equinox.concurrent"/>
- <plugin id="org.eclipse.equinox.event"/>
- <plugin id="org.eclipse.equinox.preferences"/>
- <plugin id="org.eclipse.equinox.region" fragment="true"/>
- <plugin id="org.eclipse.equinox.registry"/>
- <plugin id="org.eclipse.equinox.supplement"/>
- <plugin id="org.eclipse.equinox.transforms.hook" fragment="true"/>
- <plugin id="org.eclipse.equinox.weaving.hook" fragment="true"/>
- <plugin id="org.eclipse.fx.osgi" fragment="true"/>
- <plugin id="org.eclipse.help"/>
- <plugin id="org.eclipse.jdt.annotation"/>
- <plugin id="org.eclipse.jface"/>
- <plugin id="org.eclipse.jface.databinding"/>
- <plugin id="org.eclipse.osgi"/>
- <plugin id="org.eclipse.osgi.compatibility.state" fragment="true"/>
- <plugin id="org.eclipse.osgi.services"/>
- <plugin id="org.eclipse.osgi.util"/>
- <plugin id="org.eclipse.pde.ds.lib"/>
- <plugin id="org.eclipse.swt"/>
- <plugin id="org.eclipse.swt.cocoa.macosx.x86_64" fragment="true"/>
- <plugin id="org.eclipse.swt.gtk.linux.ppc64le" fragment="true"/>
- <plugin id="org.eclipse.swt.gtk.linux.x86_64" fragment="true"/>
- <plugin id="org.eclipse.swt.win32.win32.x86_64" fragment="true"/>
- <plugin id="org.eclipse.ui"/>
- <plugin id="org.eclipse.ui.cocoa" fragment="true"/>
- <plugin id="org.eclipse.ui.workbench"/>
- <plugin id="org.eclipse.xtext.logging" fragment="true"/>
- <plugin id="org.w3c.css.sac"/>
- <plugin id="org.w3c.dom.events"/>
- <plugin id="org.w3c.dom.smil"/>
- <plugin id="org.w3c.dom.svg"/>
- </plugins>
-
- <configurations>
- <plugin id="org.apache.felix.scr" autoStart="true" startLevel="2" />
- <plugin id="org.eclipse.core.runtime" autoStart="true" startLevel="0" />
- <plugin id="org.eclipse.equinox.common" autoStart="true" startLevel="2" />
- <plugin id="org.eclipse.equinox.event" autoStart="true" startLevel="2" />
- <plugin id="org.eclipse.equinox.simpleconfigurator" autoStart="true" startLevel="1" />
- </configurations>
-
- <preferencesInfo>
- <targetfile overwrite="false"/>
- </preferencesInfo>
-
- <cssInfo>
- <file path="/SampleERCP/css/default.css"/>
- </cssInfo>
-
-</product>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<?pde version="3.5"?>
+
+<product name="Simulator WIP" uid="sample" id="mograsim.rcp.product" application="org.eclipse.e4.ui.workbench.swt.E4Application" version="1.0.0.qualifier" useFeatures="false" includeLaunchers="true">
+
+ <aboutInfo>
+ <text>
+ Example
+ </text>
+ </aboutInfo>
+
+ <configIni use="default">
+ </configIni>
+
+ <launcherArgs>
+ <programArgs>-clearPersistedState
+ </programArgs>
+ <vmArgsMac>-XstartOnFirstThread -Dorg.eclipse.swt.internal.carbon.smallFonts
+ </vmArgsMac>
+ </launcherArgs>
+
+ <windowImages i16="/mograsim.rcp/icons/app/ico_16t.png" i32="/mograsim.rcp/icons/app/ico_32t.png" i48="/mograsim.rcp/icons/app/ico_48t.png" i64="/mograsim.rcp/icons/app/ico_64t.png" i128="/mograsim.rcp/icons/app/ico_128t.png" i256="/mograsim.rcp/icons/app/ico_256t.png"/>
+
+ <splash
+ handlerType="extensible" />
+ <launcher name="Sample">
+ <linux icon="/mograsim.rcp/icons/app/ico_256t.xpm"/>
+ <macosx icon="/mograsim.rcp/icons/app/ico_256t.icns"/>
+ <win useIco="true">
+ <ico path="/mograsim.rcp/icons/app/ico_all.ico"/>
+ <bmp/>
+ </win>
+ </launcher>
+
+
+ <vm>
+ <linux include="false">org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11</linux>
+ <macos include="false">org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11</macos>
+ <windows include="false">org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11</windows>
+ </vm>
+
+ <license>
+ <url>https://www.eclipse.org/legal/epl-2.0/</url>
+ <text>
+ Copyright (c) 2019 Christian Femers, Daniel Kirschten and Fabian Stemmler
+
+This program and the accompanying materials are made
+available under the terms of the Eclipse Public License 2.0
+which is available at https://www.eclipse.org/legal/epl-2.0/
+
+This Source Code may also be made available under the following Secondary
+Licenses when the conditions for such availability set forth in the Eclipse
+Public License, v. 2.0 are satisfied: GNU General Public License, version 2
+with the GNU Classpath Exception which is
+available at https://www.gnu.org/software/classpath/license.html.
+
+SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ </text>
+ </license>
+
+ <plugins>
+ <plugin id="SWTBufferedCanvas"/>
+ <plugin id="SWTObjectWrappers"/>
+ <plugin id="SWTZoomableCanvas"/>
+ <plugin id="com.ibm.icu"/>
+ <plugin id="javax.annotation"/>
+ <plugin id="javax.inject"/>
+ <plugin id="javax.xml"/>
+ <plugin id="mograsim.logic.core"/>
+ <plugin id="mograsim.logic.ui"/>
+ <plugin id="mograsim.rcp"/>
+ <plugin id="org.apache.batik.constants"/>
+ <plugin id="org.apache.batik.css"/>
+ <plugin id="org.apache.batik.i18n"/>
+ <plugin id="org.apache.batik.util"/>
+ <plugin id="org.apache.commons.io"/>
+ <plugin id="org.apache.commons.jxpath"/>
+ <plugin id="org.apache.commons.logging"/>
+ <plugin id="org.apache.xmlgraphics"/>
+ <plugin id="org.eclipse.core.commands"/>
+ <plugin id="org.eclipse.core.contenttype"/>
+ <plugin id="org.eclipse.core.databinding"/>
+ <plugin id="org.eclipse.core.databinding.observable"/>
+ <plugin id="org.eclipse.core.databinding.property"/>
+ <plugin id="org.eclipse.core.expressions"/>
+ <plugin id="org.eclipse.core.jobs"/>
+ <plugin id="org.eclipse.core.runtime"/>
+ <plugin id="org.eclipse.e4.core.commands"/>
+ <plugin id="org.eclipse.e4.core.contexts"/>
+ <plugin id="org.eclipse.e4.core.di"/>
+ <plugin id="org.eclipse.e4.core.di.annotations"/>
+ <plugin id="org.eclipse.e4.core.di.extensions"/>
+ <plugin id="org.eclipse.e4.core.di.extensions.supplier"/>
+ <plugin id="org.eclipse.e4.core.services"/>
+ <plugin id="org.eclipse.e4.emf.xpath"/>
+ <plugin id="org.eclipse.e4.ui.bindings"/>
+ <plugin id="org.eclipse.e4.ui.css.core"/>
+ <plugin id="org.eclipse.e4.ui.css.swt"/>
+ <plugin id="org.eclipse.e4.ui.css.swt.theme"/>
+ <plugin id="org.eclipse.e4.ui.di"/>
+ <plugin id="org.eclipse.e4.ui.model.workbench"/>
+ <plugin id="org.eclipse.e4.ui.services"/>
+ <plugin id="org.eclipse.e4.ui.widgets"/>
+ <plugin id="org.eclipse.e4.ui.workbench"/>
+ <plugin id="org.eclipse.e4.ui.workbench.addons.swt"/>
+ <plugin id="org.eclipse.e4.ui.workbench.renderers.swt"/>
+ <plugin id="org.eclipse.e4.ui.workbench.swt"/>
+ <plugin id="org.eclipse.e4.ui.workbench3"/>
+ <plugin id="org.eclipse.emf.common"/>
+ <plugin id="org.eclipse.emf.ecore"/>
+ <plugin id="org.eclipse.emf.ecore.change"/>
+ <plugin id="org.eclipse.emf.ecore.xmi"/>
+ <plugin id="org.eclipse.equinox.app"/>
+ <plugin id="org.eclipse.equinox.common"/>
+ <plugin id="org.eclipse.equinox.preferences"/>
+ <plugin id="org.eclipse.equinox.region" fragment="true"/>
+ <plugin id="org.eclipse.equinox.registry"/>
+ <plugin id="org.eclipse.equinox.supplement"/>
+ <plugin id="org.eclipse.equinox.transforms.hook" fragment="true"/>
+ <plugin id="org.eclipse.equinox.weaving.hook" fragment="true"/>
+ <plugin id="org.eclipse.help"/>
+ <plugin id="org.eclipse.jface"/>
+ <plugin id="org.eclipse.jface.databinding"/>
+ <plugin id="org.eclipse.osgi"/>
+ <plugin id="org.eclipse.osgi.compatibility.state" fragment="true"/>
+ <plugin id="org.eclipse.osgi.services"/>
+ <plugin id="org.eclipse.osgi.util"/>
+ <plugin id="org.eclipse.swt"/>
+ <plugin id="org.eclipse.swt.win32.win32.x86_64" fragment="true"/>
+ <plugin id="org.eclipse.ui.workbench"/>
+ <plugin id="org.w3c.css.sac"/>
+ <plugin id="org.w3c.dom.events"/>
+ <plugin id="org.w3c.dom.smil"/>
+ <plugin id="org.w3c.dom.svg"/>
+ </plugins>
+
+ <configurations>
+ <plugin id="org.eclipse.equinox.simpleconfigurator" autoStart="true" startLevel="1" />
+ </configurations>
+
+ <preferencesInfo>
+ <targetfile overwrite="false"/>
+ </preferencesInfo>
+
+ <cssInfo>
+ <file path="/mograsim.rcp/css/default.css"/>
+ </cssInfo>
+
+</product>
name="Simulator WIP">
<property
name="applicationCSS"
- value="platform:/plugin/SampleERCP/css/default.css">
+ value="platform:/plugin/mograsim.rcp/css/default.css">
</property>
<property
name="appName"
<extension
point="org.eclipse.ui.splashHandlers">
<splashHandler
- class="sampleercp.splashHandlers.ExtensibleSplashHandler"
- id="sampleercp.splashHandlers.extensible">
+ class="mograsim.rcp.splashhandlers.ExtensibleSplashHandler"
+ id="mograsim.rcp.splashHandlers.extensible">
</splashHandler>
<splashHandlerProductBinding
- productId="SampleERCP.product"
- splashId="sampleercp.splashHandlers.extensible">
- </splashHandlerProductBinding>
- <splashHandler
- class="sampleercp.splashhandlers.ExtensibleSplashHandler"
- id="sampleercp.splashHandlers.extensible">
- </splashHandler>
- <splashHandlerProductBinding
- productId="SampleERCP.product"
- splashId="sampleercp.splashHandlers.extensible">
+ productId="mograsim.rcp.product"
+ splashId="mograsim.rcp.splashHandlers.extensible">
</splashHandlerProductBinding>
</extension>
<extension
- point="SampleERCP.splashExtension">
+ point="mograsim.rcp.splashExtension">
<splashExtension
icon="icons/af.png"
id="af"
--- /dev/null
+package mograsim.rcp.handlers;
+
+import org.eclipse.e4.core.di.annotations.Execute;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.widgets.Shell;
+
+public class AboutHandler
+{
+ @Execute
+ public void execute(Shell shell)
+ {
+ MessageDialog.openInformation(shell, "About", "Sample RCP4");
+ }
+}
--- /dev/null
+package mograsim.rcp.handlers;
+
+import org.eclipse.e4.core.di.annotations.Execute;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Shell;
+
+public class OpenHandler
+{
+
+ @Execute
+ public void execute(Shell shell)
+ {
+ FileDialog dialog = new FileDialog(shell);
+ dialog.open();
+ }
+}
--- /dev/null
+package mograsim.rcp.handlers;
+
+import org.eclipse.e4.core.di.annotations.Execute;
+import org.eclipse.e4.ui.workbench.IWorkbench;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.widgets.Shell;
+
+public class QuitHandler
+{
+ @Execute
+ public void execute(IWorkbench workbench, Shell shell)
+ {
+ if (MessageDialog.openConfirm(shell, "Confirmation", "Do you want to exit?"))
+ {
+ workbench.close();
+ }
+ }
+}
--- /dev/null
+package mograsim.rcp.handlers;
+
+import org.eclipse.e4.core.di.annotations.CanExecute;
+import org.eclipse.e4.core.di.annotations.Execute;
+import org.eclipse.e4.ui.workbench.modeling.EPartService;
+
+public class SaveHandler
+{
+
+ @CanExecute
+ public boolean canExecute(EPartService partService)
+ {
+ if (partService != null)
+ {
+ return !partService.getDirtyParts().isEmpty();
+ }
+ return false;
+ }
+
+ @Execute
+ public void execute(EPartService partService)
+ {
+ partService.saveAll(false);
+ }
+}
\ No newline at end of file
--- /dev/null
+package mograsim.rcp.parts;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+
+import org.eclipse.e4.ui.model.application.ui.basic.MPart;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+
+import mograsim.logic.core.timeline.Timeline;
+import mograsim.logic.ui.LogicExecuter;
+import mograsim.logic.ui.LogicUICanvas;
+import mograsim.logic.ui.examples.RSLatchExample;
+import mograsim.logic.ui.model.ViewModel;
+import mograsim.logic.ui.modeladapter.LogicModelParameters;
+import mograsim.logic.ui.modeladapter.ViewLogicModelAdapter;
+import net.haspamelodica.swt.helper.zoomablecanvas.helper.ZoomableCanvasUserInput;
+
+public class LogicUIPart
+{
+ @Inject
+ private MPart part;
+
+ @PostConstruct
+ public void create(Composite parent)
+ {
+ // setup view model
+ ViewModel viewModel = new ViewModel();
+ RSLatchExample.createRSLatchExample(viewModel);
+
+ // convert to logic model
+ LogicModelParameters params = new LogicModelParameters();
+ params.gateProcessTime = 50;
+ params.wireTravelTime = 10;
+ Timeline timeline = ViewLogicModelAdapter.convert(viewModel, params);
+
+ // initialize UI
+ LogicUICanvas ui = new LogicUICanvas(parent, SWT.NONE, viewModel);
+ ui.addTransformListener((x, y, z) -> part.setDirty(z < 1));
+ ZoomableCanvasUserInput userInput = new ZoomableCanvasUserInput(ui);
+ userInput.buttonDrag = 3;
+ userInput.buttonZoom = 2;
+ userInput.enableUserInput();
+
+ // initialize executer
+ LogicExecuter exec = new LogicExecuter(timeline);
+
+ // run it
+ exec.startLiveExecution();
+
+ // TODO find a better condition when to stop
+ ui.addDisposeListener(e -> exec.stopLiveExecution());
+ }
+}
\ No newline at end of file
--- /dev/null
+package mograsim.rcp.parts;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+
+import org.eclipse.e4.ui.di.Focus;
+import org.eclipse.e4.ui.di.Persist;
+import org.eclipse.e4.ui.model.application.ui.basic.MPart;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Text;
+
+public class SamplePart
+{
+
+ private TableViewer tableViewer;
+
+ @Inject
+ private MPart part;
+
+ @PostConstruct
+ public void createComposite(Composite parent)
+ {
+ parent.setLayout(new GridLayout(1, false));
+
+ Text txtInput = new Text(parent, SWT.BORDER);
+ txtInput.setMessage("Enter text to mark part as dirty");
+ txtInput.addModifyListener(e -> part.setDirty(true));
+ txtInput.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+ tableViewer = new TableViewer(parent);
+
+ tableViewer.setContentProvider(ArrayContentProvider.getInstance());
+ tableViewer.setInput(createInitialDataModel());
+ tableViewer.getTable().setLayoutData(new GridData(GridData.FILL_BOTH));
+ }
+
+ @Focus
+ public void setFocus()
+ {
+ tableViewer.getTable().setFocus();
+ }
+
+ @Persist
+ public void save()
+ {
+ part.setDirty(false);
+ }
+
+ private static List<String> createInitialDataModel()
+ {
+ return Arrays.asList("Sample item 1", "Sample item 2", "Sample item 3", "Sample item 4", "Sample item 5");
+ }
+}
\ No newline at end of file
--- /dev/null
+package mograsim.rcp.splashhandlers;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.eclipse.ui.splash.AbstractSplashHandler;
+
+/**
+ * @since 3.3
+ *
+ */
+public class ExtensibleSplashHandler extends AbstractSplashHandler
+{
+
+ private ArrayList<Image> fImageList;
+
+ private ArrayList<String> fTooltipList;
+
+ private static final String F_SPLASH_EXTENSION_ID = "Sample.splashExtension"; // NON-NLS-1
+
+ private static final String F_ELEMENT_ICON = "icon"; // NON-NLS-1
+
+ private static final String F_ELEMENT_TOOLTIP = "tooltip"; // NON-NLS-1
+
+ private static final String F_DEFAULT_TOOLTIP = "Image"; // NON-NLS-1
+
+ private static final int F_IMAGE_WIDTH = 50;
+
+ private static final int F_IMAGE_HEIGHT = 50;
+
+ private static final int F_SPLASH_SCREEN_BEVEL = 5;
+
+ private Composite fIconPanel;
+
+ /**
+ *
+ */
+ public ExtensibleSplashHandler()
+ {
+ fImageList = new ArrayList<>();
+ fTooltipList = new ArrayList<>();
+ fIconPanel = null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.splash.AbstractSplashHandler#init(org.eclipse.swt.widgets. Shell)
+ */
+ @Override
+ public void init(Shell splash)
+ {
+ // Store the shell
+ super.init(splash);
+ // Configure the shell layout
+ configureUISplash();
+ // Load all splash extensions
+ loadSplashExtensions();
+ // If no splash extensions were loaded abort the splash handler
+ if (!hasSplashExtensions())
+ {
+ return;
+ }
+ // Create UI
+ createUI();
+ // Configure the image panel bounds
+ configureUICompositeIconPanelBounds();
+ // Enter event loop and prevent the RCP application from
+ // loading until all work is done
+ doEventLoop();
+ }
+
+ /**
+ * @return
+ */
+ private boolean hasSplashExtensions()
+ {
+ return !fImageList.isEmpty();
+ }
+
+ /**
+ *
+ */
+ private void createUI()
+ {
+ // Create the icon panel
+ createUICompositeIconPanel();
+ // Create the images
+ createUIImages();
+ }
+
+ /**
+ *
+ */
+ private void createUIImages()
+ {
+ Iterator<Image> imageIterator = fImageList.iterator();
+ Iterator<String> tooltipIterator = fTooltipList.iterator();
+ int i = 1;
+ int columnCount = ((GridLayout) fIconPanel.getLayout()).numColumns;
+ // Create all the images
+ // Abort if we run out of columns (left-over images will not fit within
+ // the usable splash screen width)
+ while (imageIterator.hasNext() && (i <= columnCount))
+ {
+ Image image = imageIterator.next();
+ String tooltip = tooltipIterator.next();
+ // Create the image using a label widget
+ createUILabel(image, tooltip);
+ i++;
+ }
+ }
+
+ /**
+ * @param image
+ * @param tooltip
+ */
+ private void createUILabel(Image image, String tooltip)
+ {
+ // Create the label (no text)
+ Label label = new Label(fIconPanel, SWT.NONE);
+ label.setImage(image);
+ label.setToolTipText(tooltip);
+ }
+
+ /**
+ *
+ */
+ private void createUICompositeIconPanel()
+ {
+ Shell splash = getSplash();
+ // Create the composite
+ fIconPanel = new Composite(splash, SWT.NONE);
+ // Determine the maximum number of columns that can fit on the splash
+ // screen. One 50x50 image per column.
+ int maxColumnCount = getUsableSplashScreenWidth() / F_IMAGE_WIDTH;
+ // Limit size to the maximum number of columns if the number of images
+ // exceed this amount; otherwise, use the exact number of columns
+ // required.
+ int actualColumnCount = Math.min(fImageList.size(), maxColumnCount);
+ // Configure the layout
+ GridLayout layout = new GridLayout(actualColumnCount, true);
+ layout.horizontalSpacing = 0;
+ layout.verticalSpacing = 0;
+ layout.marginHeight = 0;
+ layout.marginWidth = 0;
+ fIconPanel.setLayout(layout);
+ }
+
+ /**
+ *
+ */
+ private void configureUICompositeIconPanelBounds()
+ {
+ // Determine the size of the panel and position it at the bottom-right
+ // of the splash screen.
+ Point panelSize = fIconPanel.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
+
+ int xCoord = getSplash().getSize().x - F_SPLASH_SCREEN_BEVEL - panelSize.x;
+ int yCoord = getSplash().getSize().y - F_SPLASH_SCREEN_BEVEL - panelSize.y;
+ int xWidth = panelSize.x;
+ int yWidth = panelSize.y;
+
+ fIconPanel.setBounds(xCoord, yCoord, xWidth, yWidth);
+ }
+
+ /**
+ * @return
+ */
+ private int getUsableSplashScreenWidth()
+ {
+ // Splash screen width minus two graphic border bevel widths
+ return getSplash().getSize().x - (F_SPLASH_SCREEN_BEVEL * 2);
+ }
+
+ /**
+ *
+ */
+ private void loadSplashExtensions()
+ {
+ // Get all splash handler extensions
+ IExtension[] extensions = Platform.getExtensionRegistry().getExtensionPoint(F_SPLASH_EXTENSION_ID).getExtensions();
+ // Process all splash handler extensions
+ for (int i = 0; i < extensions.length; i++)
+ {
+ processSplashExtension(extensions[i]);
+ }
+ }
+
+ /**
+ * @param extension
+ */
+ private void processSplashExtension(IExtension extension)
+ {
+ // Get all splash handler configuration elements
+ IConfigurationElement[] elements = extension.getConfigurationElements();
+ // Process all splash handler configuration elements
+ for (int j = 0; j < elements.length; j++)
+ {
+ processSplashElements(elements[j]);
+ }
+ }
+
+ /**
+ * @param configurationElement
+ */
+ private void processSplashElements(IConfigurationElement configurationElement)
+ {
+ // Attribute: icon
+ processSplashElementIcon(configurationElement);
+ // Attribute: tooltip
+ processSplashElementTooltip(configurationElement);
+ }
+
+ /**
+ * @param configurationElement
+ */
+ private void processSplashElementTooltip(IConfigurationElement configurationElement)
+ {
+ // Get attribute tooltip
+ String tooltip = configurationElement.getAttribute(F_ELEMENT_TOOLTIP);
+ // If a tooltip is not defined, give it a default
+ if ((tooltip == null) || (tooltip.length() == 0))
+ {
+ fTooltipList.add(F_DEFAULT_TOOLTIP);
+ } else
+ {
+ fTooltipList.add(tooltip);
+ }
+ }
+
+ /**
+ * @param configurationElement
+ */
+ private void processSplashElementIcon(IConfigurationElement configurationElement)
+ {
+ // Get attribute icon
+ String iconImageFilePath = configurationElement.getAttribute(F_ELEMENT_ICON);
+ // Abort if an icon attribute was not specified
+ if ((iconImageFilePath == null) || (iconImageFilePath.length() == 0))
+ {
+ return;
+ }
+ // Create a corresponding image descriptor
+ ImageDescriptor descriptor = AbstractUIPlugin.imageDescriptorFromPlugin(configurationElement.getNamespaceIdentifier(),
+ iconImageFilePath);
+ // Abort if no corresponding image was found
+ if (descriptor == null)
+ {
+ return;
+ }
+ // Create the image
+ Image image = descriptor.createImage();
+ // Abort if image creation failed
+ if (image == null)
+ {
+ return;
+ }
+ // Abort if the image does not have dimensions of 50x50
+ if ((image.getBounds().width != F_IMAGE_WIDTH) || (image.getBounds().height != F_IMAGE_HEIGHT))
+ {
+ // Dipose of the image
+ image.dispose();
+ return;
+ }
+ // Store the image and tooltip
+ fImageList.add(image);
+ }
+
+ /**
+ *
+ */
+ private void configureUISplash()
+ {
+ // Configure layout
+ GridLayout layout = new GridLayout(1, true);
+ getSplash().setLayout(layout);
+ // Force shell to inherit the splash background
+ getSplash().setBackgroundMode(SWT.INHERIT_DEFAULT);
+ }
+
+ /**
+ *
+ */
+ private void doEventLoop()
+ {
+ Shell splash = getSplash();
+ if (!splash.getDisplay().readAndDispatch())
+ {
+ splash.getDisplay().sleep();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.splash.AbstractSplashHandler#dispose()
+ */
+ @Override
+ public void dispose()
+ {
+ super.dispose();
+ // Check to see if any images were defined
+ if ((fImageList == null) || fImageList.isEmpty())
+ {
+ return;
+ }
+ // Dispose of all the images
+ Iterator<Image> iterator = fImageList.iterator();
+ while (iterator.hasNext())
+ {
+ Image image = iterator.next();
+ image.dispose();
+ }
+ }
+}
+++ /dev/null
-package sampleercp.handlers;
-
-import org.eclipse.e4.core.di.annotations.Execute;
-import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.swt.widgets.Shell;
-
-public class AboutHandler
-{
- @Execute
- public void execute(Shell shell)
- {
- MessageDialog.openInformation(shell, "About", "Sample RCP4");
- }
-}
+++ /dev/null
-package sampleercp.handlers;
-
-import org.eclipse.e4.core.di.annotations.Execute;
-import org.eclipse.swt.widgets.FileDialog;
-import org.eclipse.swt.widgets.Shell;
-
-public class OpenHandler
-{
-
- @Execute
- public void execute(Shell shell)
- {
- FileDialog dialog = new FileDialog(shell);
- dialog.open();
- }
-}
+++ /dev/null
-package sampleercp.handlers;
-
-import org.eclipse.e4.core.di.annotations.Execute;
-import org.eclipse.e4.ui.workbench.IWorkbench;
-import org.eclipse.jface.dialogs.MessageDialog;
-import org.eclipse.swt.widgets.Shell;
-
-public class QuitHandler
-{
- @Execute
- public void execute(IWorkbench workbench, Shell shell)
- {
- if (MessageDialog.openConfirm(shell, "Confirmation", "Do you want to exit?"))
- {
- workbench.close();
- }
- }
-}
+++ /dev/null
-package sampleercp.handlers;
-
-import org.eclipse.e4.core.di.annotations.CanExecute;
-import org.eclipse.e4.core.di.annotations.Execute;
-import org.eclipse.e4.ui.workbench.modeling.EPartService;
-
-public class SaveHandler
-{
-
- @CanExecute
- public boolean canExecute(EPartService partService)
- {
- if (partService != null)
- {
- return !partService.getDirtyParts().isEmpty();
- }
- return false;
- }
-
- @Execute
- public void execute(EPartService partService)
- {
- partService.saveAll(false);
- }
-}
\ No newline at end of file
+++ /dev/null
-package sampleercp.parts;
-
-import javax.annotation.PostConstruct;
-import javax.inject.Inject;
-
-import org.eclipse.e4.ui.model.application.ui.basic.MPart;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Composite;
-
-import era.mi.gui.LogicExecuter;
-import era.mi.gui.LogicUICanvas;
-import era.mi.gui.examples.RSLatchExample;
-import era.mi.gui.model.ViewModel;
-import era.mi.gui.modeladapter.LogicModelParameters;
-import era.mi.gui.modeladapter.ViewLogicModelAdapter;
-import era.mi.logic.timeline.Timeline;
-import net.haspamelodica.swt.helper.zoomablecanvas.helper.ZoomableCanvasUserInput;
-
-public class LogicUIPart
-{
- @Inject
- private MPart part;
-
- @PostConstruct
- public void create(Composite parent)
- {
- // setup view model
- ViewModel viewModel = new ViewModel();
- RSLatchExample.createRSLatchExample(viewModel);
-
- // convert to logic model
- LogicModelParameters params = new LogicModelParameters();
- params.gateProcessTime = 50;
- params.wireTravelTime = 10;
- Timeline timeline = ViewLogicModelAdapter.convert(viewModel, params);
-
- // initialize UI
- LogicUICanvas ui = new LogicUICanvas(parent, SWT.NONE, viewModel);
- ui.addTransformListener((x, y, z) -> part.setDirty(z < 1));
- ZoomableCanvasUserInput userInput = new ZoomableCanvasUserInput(ui);
- userInput.buttonDrag = 3;
- userInput.buttonZoom = 2;
- userInput.enableUserInput();
-
- // initialize executer
- LogicExecuter exec = new LogicExecuter(timeline);
-
- // run it
- exec.startLiveExecution();
-
- // TODO find a better condition when to stop
- ui.addDisposeListener(e -> exec.stopLiveExecution());
- }
-}
\ No newline at end of file
+++ /dev/null
-package sampleercp.parts;
-
-import java.util.Arrays;
-import java.util.List;
-
-import javax.annotation.PostConstruct;
-import javax.inject.Inject;
-
-import org.eclipse.e4.ui.di.Focus;
-import org.eclipse.e4.ui.di.Persist;
-import org.eclipse.e4.ui.model.application.ui.basic.MPart;
-import org.eclipse.jface.viewers.ArrayContentProvider;
-import org.eclipse.jface.viewers.TableViewer;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Text;
-
-public class SamplePart
-{
-
- private TableViewer tableViewer;
-
- @Inject
- private MPart part;
-
- @PostConstruct
- public void createComposite(Composite parent)
- {
- parent.setLayout(new GridLayout(1, false));
-
- Text txtInput = new Text(parent, SWT.BORDER);
- txtInput.setMessage("Enter text to mark part as dirty");
- txtInput.addModifyListener(e -> part.setDirty(true));
- txtInput.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
-
- tableViewer = new TableViewer(parent);
-
- tableViewer.setContentProvider(ArrayContentProvider.getInstance());
- tableViewer.setInput(createInitialDataModel());
- tableViewer.getTable().setLayoutData(new GridData(GridData.FILL_BOTH));
- }
-
- @Focus
- public void setFocus()
- {
- tableViewer.getTable().setFocus();
- }
-
- @Persist
- public void save()
- {
- part.setDirty(false);
- }
-
- private static List<String> createInitialDataModel()
- {
- return Arrays.asList("Sample item 1", "Sample item 2", "Sample item 3", "Sample item 4", "Sample item 5");
- }
-}
\ No newline at end of file
+++ /dev/null
-package sampleercp.splashhandlers;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-
-import org.eclipse.core.runtime.IConfigurationElement;
-import org.eclipse.core.runtime.IExtension;
-import org.eclipse.core.runtime.Platform;
-import org.eclipse.jface.resource.ImageDescriptor;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Shell;
-import org.eclipse.ui.plugin.AbstractUIPlugin;
-import org.eclipse.ui.splash.AbstractSplashHandler;
-
-/**
- * @since 3.3
- *
- */
-public class ExtensibleSplashHandler extends AbstractSplashHandler
-{
-
- private ArrayList<Image> fImageList;
-
- private ArrayList<String> fTooltipList;
-
- private static final String F_SPLASH_EXTENSION_ID = "Sample.splashExtension"; // NON-NLS-1
-
- private static final String F_ELEMENT_ICON = "icon"; // NON-NLS-1
-
- private static final String F_ELEMENT_TOOLTIP = "tooltip"; // NON-NLS-1
-
- private static final String F_DEFAULT_TOOLTIP = "Image"; // NON-NLS-1
-
- private static final int F_IMAGE_WIDTH = 50;
-
- private static final int F_IMAGE_HEIGHT = 50;
-
- private static final int F_SPLASH_SCREEN_BEVEL = 5;
-
- private Composite fIconPanel;
-
- /**
- *
- */
- public ExtensibleSplashHandler()
- {
- fImageList = new ArrayList<>();
- fTooltipList = new ArrayList<>();
- fIconPanel = null;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see org.eclipse.ui.splash.AbstractSplashHandler#init(org.eclipse.swt.widgets. Shell)
- */
- @Override
- public void init(Shell splash)
- {
- // Store the shell
- super.init(splash);
- // Configure the shell layout
- configureUISplash();
- // Load all splash extensions
- loadSplashExtensions();
- // If no splash extensions were loaded abort the splash handler
- if (!hasSplashExtensions())
- {
- return;
- }
- // Create UI
- createUI();
- // Configure the image panel bounds
- configureUICompositeIconPanelBounds();
- // Enter event loop and prevent the RCP application from
- // loading until all work is done
- doEventLoop();
- }
-
- /**
- * @return
- */
- private boolean hasSplashExtensions()
- {
- return !fImageList.isEmpty();
- }
-
- /**
- *
- */
- private void createUI()
- {
- // Create the icon panel
- createUICompositeIconPanel();
- // Create the images
- createUIImages();
- }
-
- /**
- *
- */
- private void createUIImages()
- {
- Iterator<Image> imageIterator = fImageList.iterator();
- Iterator<String> tooltipIterator = fTooltipList.iterator();
- int i = 1;
- int columnCount = ((GridLayout) fIconPanel.getLayout()).numColumns;
- // Create all the images
- // Abort if we run out of columns (left-over images will not fit within
- // the usable splash screen width)
- while (imageIterator.hasNext() && (i <= columnCount))
- {
- Image image = imageIterator.next();
- String tooltip = tooltipIterator.next();
- // Create the image using a label widget
- createUILabel(image, tooltip);
- i++;
- }
- }
-
- /**
- * @param image
- * @param tooltip
- */
- private void createUILabel(Image image, String tooltip)
- {
- // Create the label (no text)
- Label label = new Label(fIconPanel, SWT.NONE);
- label.setImage(image);
- label.setToolTipText(tooltip);
- }
-
- /**
- *
- */
- private void createUICompositeIconPanel()
- {
- Shell splash = getSplash();
- // Create the composite
- fIconPanel = new Composite(splash, SWT.NONE);
- // Determine the maximum number of columns that can fit on the splash
- // screen. One 50x50 image per column.
- int maxColumnCount = getUsableSplashScreenWidth() / F_IMAGE_WIDTH;
- // Limit size to the maximum number of columns if the number of images
- // exceed this amount; otherwise, use the exact number of columns
- // required.
- int actualColumnCount = Math.min(fImageList.size(), maxColumnCount);
- // Configure the layout
- GridLayout layout = new GridLayout(actualColumnCount, true);
- layout.horizontalSpacing = 0;
- layout.verticalSpacing = 0;
- layout.marginHeight = 0;
- layout.marginWidth = 0;
- fIconPanel.setLayout(layout);
- }
-
- /**
- *
- */
- private void configureUICompositeIconPanelBounds()
- {
- // Determine the size of the panel and position it at the bottom-right
- // of the splash screen.
- Point panelSize = fIconPanel.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
-
- int xCoord = getSplash().getSize().x - F_SPLASH_SCREEN_BEVEL - panelSize.x;
- int yCoord = getSplash().getSize().y - F_SPLASH_SCREEN_BEVEL - panelSize.y;
- int xWidth = panelSize.x;
- int yWidth = panelSize.y;
-
- fIconPanel.setBounds(xCoord, yCoord, xWidth, yWidth);
- }
-
- /**
- * @return
- */
- private int getUsableSplashScreenWidth()
- {
- // Splash screen width minus two graphic border bevel widths
- return getSplash().getSize().x - (F_SPLASH_SCREEN_BEVEL * 2);
- }
-
- /**
- *
- */
- private void loadSplashExtensions()
- {
- // Get all splash handler extensions
- IExtension[] extensions = Platform.getExtensionRegistry().getExtensionPoint(F_SPLASH_EXTENSION_ID).getExtensions();
- // Process all splash handler extensions
- for (int i = 0; i < extensions.length; i++)
- {
- processSplashExtension(extensions[i]);
- }
- }
-
- /**
- * @param extension
- */
- private void processSplashExtension(IExtension extension)
- {
- // Get all splash handler configuration elements
- IConfigurationElement[] elements = extension.getConfigurationElements();
- // Process all splash handler configuration elements
- for (int j = 0; j < elements.length; j++)
- {
- processSplashElements(elements[j]);
- }
- }
-
- /**
- * @param configurationElement
- */
- private void processSplashElements(IConfigurationElement configurationElement)
- {
- // Attribute: icon
- processSplashElementIcon(configurationElement);
- // Attribute: tooltip
- processSplashElementTooltip(configurationElement);
- }
-
- /**
- * @param configurationElement
- */
- private void processSplashElementTooltip(IConfigurationElement configurationElement)
- {
- // Get attribute tooltip
- String tooltip = configurationElement.getAttribute(F_ELEMENT_TOOLTIP);
- // If a tooltip is not defined, give it a default
- if ((tooltip == null) || (tooltip.length() == 0))
- {
- fTooltipList.add(F_DEFAULT_TOOLTIP);
- } else
- {
- fTooltipList.add(tooltip);
- }
- }
-
- /**
- * @param configurationElement
- */
- private void processSplashElementIcon(IConfigurationElement configurationElement)
- {
- // Get attribute icon
- String iconImageFilePath = configurationElement.getAttribute(F_ELEMENT_ICON);
- // Abort if an icon attribute was not specified
- if ((iconImageFilePath == null) || (iconImageFilePath.length() == 0))
- {
- return;
- }
- // Create a corresponding image descriptor
- ImageDescriptor descriptor = AbstractUIPlugin.imageDescriptorFromPlugin(configurationElement.getNamespaceIdentifier(),
- iconImageFilePath);
- // Abort if no corresponding image was found
- if (descriptor == null)
- {
- return;
- }
- // Create the image
- Image image = descriptor.createImage();
- // Abort if image creation failed
- if (image == null)
- {
- return;
- }
- // Abort if the image does not have dimensions of 50x50
- if ((image.getBounds().width != F_IMAGE_WIDTH) || (image.getBounds().height != F_IMAGE_HEIGHT))
- {
- // Dipose of the image
- image.dispose();
- return;
- }
- // Store the image and tooltip
- fImageList.add(image);
- }
-
- /**
- *
- */
- private void configureUISplash()
- {
- // Configure layout
- GridLayout layout = new GridLayout(1, true);
- getSplash().setLayout(layout);
- // Force shell to inherit the splash background
- getSplash().setBackgroundMode(SWT.INHERIT_DEFAULT);
- }
-
- /**
- *
- */
- private void doEventLoop()
- {
- Shell splash = getSplash();
- if (!splash.getDisplay().readAndDispatch())
- {
- splash.getDisplay().sleep();
- }
- }
-
- /*
- * (non-Javadoc)
- *
- * @see org.eclipse.ui.splash.AbstractSplashHandler#dispose()
- */
- @Override
- public void dispose()
- {
- super.dispose();
- // Check to see if any images were defined
- if ((fImageList == null) || fImageList.isEmpty())
- {
- return;
- }
- // Dispose of all the images
- Iterator<Image> iterator = fImageList.iterator();
- while (iterator.hasNext())
- {
- Image image = iterator.next();
- image.dispose();
- }
- }
-}
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
- <name>era.mi</name>
+ <name>mograsim.logic.core</name>
<comment></comment>
<projects>
</projects>
org.eclipse.jdt.core.compiler.annotation.nullable.secondary=
org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
org.eclipse.jdt.core.compiler.codegen.targetPlatform=10
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=10
eclipse.preferences.version=1
editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
-formatter_profile=_ERA-MI
+formatter_profile=_MoGraSim
formatter_settings_version=16
sp_cleanup.add_default_serial_version_id=true
sp_cleanup.add_generated_serial_version_id=false
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
-Bundle-Name: MI
-Bundle-SymbolicName: era.mi
+Bundle-Name: mograsim.logic.core
+Bundle-SymbolicName: mograsim.logic.core
Bundle-Version: 1.0.0.qualifier
-Export-Package: era.mi.logic,
- era.mi.logic.components,
- era.mi.logic.components.gates,
- era.mi.logic.tests,
- era.mi.logic.timeline,
- era.mi.logic.types,
- era.mi.logic.wires
+Export-Package: mograsim.logic.core,
+ mograsim.logic.core.components,
+ mograsim.logic.core.components.gates,
+ mograsim.logic.core.tests,
+ mograsim.logic.core.timeline,
+ mograsim.logic.core.types,
+ mograsim.logic.core.wires
Bundle-RequiredExecutionEnvironment: JavaSE-10
-Automatic-Module-Name: era.mi
+Automatic-Module-Name: mograsim.logic.core
+++ /dev/null
-package era.mi.logic;
-
-import java.util.Arrays;
-
-import era.mi.logic.types.Bit;
-
-public final class Util
-{
-
- @SuppressWarnings("unchecked")
- public static <T> T[] concat(T[]... arrays)
- {
- if (arrays.length == 0)
- throw new IllegalArgumentException("Cannot concatenate 0 arrays.");
-
- int length = 0;
- for (T[] array : arrays)
- length += array.length;
-
- T[] newArray = Arrays.copyOf(arrays[0], length);
- int appendIndex = arrays[0].length;
- for (int i = 1; i < arrays.length; i++)
- {
- System.arraycopy(arrays[i], 0, newArray, appendIndex, arrays[i].length);
- appendIndex += arrays[i].length;
- }
-
- return newArray;
- }
-
-// @SuppressWarnings("unchecked")
-// public static <T> T[][] split(T[] array, int... lengths)
-// {
-// //TODO: implement array split again; This version contains an illegal cast
-// int totalLength = 0;
-// for(int length : lengths)
-// totalLength += length;
-//
-// if(totalLength != array.length)
-// throw new IllegalArgumentException(); //TODO: add proper error message
-//
-// Object[][] newArray = new Object[lengths.length][];
-// int splitIndex = 0;
-// for(int i = 0; i < lengths.length; i++)
-// {
-// System.arraycopy(array, splitIndex, newArray, 0, lengths[i]);
-// splitIndex += lengths[i];
-// }
-//
-// return (T[][]) newArray;
-// }
-
- public static Bit[] and(Bit[] a, Bit[] b)
- {
- return binBitOp(a, b, Bit::and);
- }
-
- public static Bit[] or(Bit[] a, Bit[] b)
- {
- return binBitOp(a, b, Bit::or);
- }
-
- public static Bit[] xor(Bit[] a, Bit[] b)
- {
- return binBitOp(a, b, Bit::xor);
- }
-
- private static Bit[] binBitOp(Bit[] a, Bit[] b, BitOp op)
- {
- if (a.length != b.length)
- throw new IllegalArgumentException("Bit Arrays were not of equal length.");
- Bit[] out = new Bit[a.length];
- for (int i = 0; i < a.length; i++)
- {
- out[i] = op.execute(a[i], b[i]);
- }
- return out;
- }
-
- public static Bit[] not(Bit[] a)
- {
- Bit[] out = new Bit[a.length];
- for (int i = 0; i < a.length; i++)
- {
- out[i] = a[i].not();
- }
- return out;
- }
-
- /**
- * uses the {@link Bit#combineWith(Bit)} method, does not create a new array, the result is stored in the first array.
- *
- * @author Christian Femers
- */
- public static Bit[] combineInto(Bit[] dest, Bit[] addition)
- {
- if (dest.length != addition.length)
- throw new IllegalArgumentException("Bit Arrays were not of equal length.");
- for (int i = 0; i < addition.length; i++)
- {
- dest[i] = dest[i].join(addition[i]);
- }
- return dest;
- }
-
- interface BitOp
- {
- Bit execute(Bit a, Bit b);
- }
-}
+++ /dev/null
-package era.mi.logic.components;
-
-import era.mi.logic.timeline.Timeline;
-import era.mi.logic.types.BitVector;
-import era.mi.logic.wires.Wire.ReadEnd;
-import era.mi.logic.wires.WireObserver;
-
-/**
- * A basic component that recomputes all outputs (with a delay), when it is updated.
- *
- * @author Fabian Stemmler
- */
-public abstract class BasicComponent extends Component implements WireObserver
-{
- private int processTime;
-
- /**
- *
- * @param processTime Amount of time this component takes to update its outputs. Must be more than 0, otherwise 1 is assumed.
- *
- * @author Fabian Stemmler
- */
- public BasicComponent(Timeline timeline, int processTime)
- {
- super(timeline);
- this.processTime = processTime > 0 ? processTime : 1;
- }
-
- @Override
- public void update(ReadEnd initiator, BitVector oldValues)
- {
- timeline.addEvent(e -> compute(), processTime);
- }
-
- protected abstract void compute();
-}
+++ /dev/null
-package era.mi.logic.components;
-
-import java.util.List;
-
-import era.mi.logic.timeline.Timeline;
-import era.mi.logic.types.Bit;
-import era.mi.logic.types.BitVector;
-import era.mi.logic.wires.Wire.ReadEnd;
-import era.mi.logic.wires.Wire.ReadWriteEnd;
-
-public class BitDisplay extends BasicComponent
-{
- private final ReadEnd in;
- private BitVector displayedValue;
-
- public BitDisplay(Timeline timeline, ReadEnd in)
- {
- super(timeline, 1);
- this.in = in;
- in.addObserver(this);
- compute();
- }
-
- @Override
- protected void compute()
- {
- displayedValue = in.getValues();
- }
-
- public BitVector getDisplayedValue()
- {
- return displayedValue;
- }
-
- public boolean isDisplaying(Bit... values)
- {
- return displayedValue.equals(BitVector.of(values));
- }
-
- @Override
- public List<ReadEnd> getAllInputs()
- {
- return List.of(in);
- }
-
- @Override
- public List<ReadWriteEnd> getAllOutputs()
- {
- return List.of();
- }
-}
+++ /dev/null
-package era.mi.logic.components;
-
-import java.util.List;
-
-import era.mi.logic.timeline.Timeline;
-import era.mi.logic.timeline.TimelineEvent;
-import era.mi.logic.timeline.TimelineEventHandler;
-import era.mi.logic.types.Bit;
-import era.mi.logic.wires.Wire;
-import era.mi.logic.wires.Wire.ReadEnd;
-import era.mi.logic.wires.Wire.ReadWriteEnd;
-
-public class Clock extends Component implements TimelineEventHandler
-{
- private boolean toggle = false;
- private ReadWriteEnd out;
- private int delta;
-
- /**
- *
- * @param out {@link Wire} the clock's impulses are fed into
- * @param delta ticks between rising and falling edge
- */
- public Clock(Timeline timeline, ReadWriteEnd out, int delta)
- {
- super(timeline);
- this.delta = delta;
- this.out = out;
- addToTimeline();
- }
-
- @Override
- public void handle(TimelineEvent e)
- {
- addToTimeline();
- out.feedSignals(toggle ? Bit.ONE : Bit.ZERO);
- toggle = !toggle;
- }
-
- public ReadWriteEnd getOut()
- {
- return out;
- }
-
- private void addToTimeline()
- {
- timeline.addEvent(this, delta);
- }
-
- @Override
- public List<ReadEnd> getAllInputs()
- {
- return List.of();
- }
-
- @Override
- public List<ReadWriteEnd> getAllOutputs()
- {
- return List.of(out);
- }
-}
+++ /dev/null
-package era.mi.logic.components;
-
-import java.util.List;
-
-import era.mi.logic.timeline.Timeline;
-import era.mi.logic.wires.Wire.ReadEnd;
-import era.mi.logic.wires.Wire.ReadWriteEnd;
-
-public abstract class Component
-{
- protected Timeline timeline;
-
- public Component(Timeline timeline)
- {
- this.timeline = timeline;
- }
-
- /**
- * Returns immutable list of all inputs to the {@link Component} (including e.g. the select bits to a MUX). Intended for visualization
- * in the UI.
- */
- public abstract List<ReadEnd> getAllInputs();
-
- /**
- * Returns immutable list of all outputs to the {@link Component}. Intended for visualization in the UI.
- */
- public abstract List<ReadWriteEnd> getAllOutputs();
-}
+++ /dev/null
-package era.mi.logic.components;
-
-import java.util.List;
-
-import era.mi.logic.timeline.Timeline;
-import era.mi.logic.types.BitVector;
-import era.mi.logic.wires.Wire.ReadEnd;
-import era.mi.logic.wires.Wire.ReadWriteEnd;
-import era.mi.logic.wires.WireObserver;
-
-public class Connector extends Component implements WireObserver
-{
- private boolean connected;
- private final ReadWriteEnd a;
- private final ReadWriteEnd b;
-
- public Connector(Timeline timeline, ReadWriteEnd a, ReadWriteEnd b)
- {
- super(timeline);
- if (a.length() != b.length())
- throw new IllegalArgumentException(String.format("WireArray width does not match: %d, %d", a.length(), b.length()));
- this.a = a;
- this.b = b;
- a.addObserver(this);
- b.addObserver(this);
- }
-
- public void connect()
- {
- connected = true;
- update(a);
- update(b);
- }
-
- public void disconnect()
- {
- connected = false;
- a.clearSignals();
- b.clearSignals();
- }
-
- public void setConnection(boolean connected)
- {
- if (connected)
- connect();
- else
- disconnect();
- }
-
- @Override
- public void update(ReadEnd initiator, BitVector oldValues)
- {
- if (connected)
- timeline.addEvent(e -> update(initiator), 1);
- }
-
- private void update(ReadEnd initiator)
- {
- if (initiator == a)
- b.feedSignals(a.wireValuesExcludingMe());
- else
- a.feedSignals(b.wireValuesExcludingMe());
- }
-
- @Override
- public List<ReadEnd> getAllInputs()
- {
- return List.of(a, b);
- }
-
- @Override
- public List<ReadWriteEnd> getAllOutputs()
- {
- return List.of(a, b);
- }
-}
+++ /dev/null
-package era.mi.logic.components;
-
-import java.util.List;
-
-import era.mi.logic.timeline.Timeline;
-import era.mi.logic.wires.Wire;
-import era.mi.logic.wires.Wire.ReadEnd;
-import era.mi.logic.wires.Wire.ReadWriteEnd;
-
-/**
- * Models a multiplexer. Takes an arbitrary amount of input {@link Wire}s, one of which, as determined by select, is put through to the
- * output.
- *
- * @author Fabian Stemmler
- *
- */
-public class Demux extends BasicComponent
-{
- private final ReadEnd select, in;
- private final ReadWriteEnd[] outputs;
- private final int outputSize;
- private int selected = -1;
-
- /**
- * Output {@link Wire}s and in must be of uniform length
- *
- * @param in Must be of uniform length with all outputs.
- * @param select Indexes the output array to which the input is mapped. Must have enough bits to index all outputs.
- * @param outputs One of these outputs receives the input signal, depending on the select bits
- */
- public Demux(Timeline timeline, int processTime, ReadEnd in, ReadEnd select, ReadWriteEnd... outputs)
- {
- super(timeline, processTime);
- outputSize = in.length();
-
- this.in = in;
- this.outputs = outputs;
- for (int i = 0; i < this.outputs.length; i++)
- {
- if (outputs[i].length() != outputSize)
- throw new IllegalArgumentException("All DEMUX wire arrays must be of uniform length!");
- this.outputs[i] = outputs[i];
- }
-
- this.select = select;
- select.addObserver(this);
-
- int maxInputs = 1 << select.length();
- if (this.outputs.length > maxInputs)
- throw new IllegalArgumentException("There are more outputs (" + this.outputs.length + ") to the DEMUX than supported by "
- + select.length() + " select bits (" + maxInputs + ").");
- in.addObserver(this);
- }
-
- @Override
- public void compute()
- {
- int selectValue = select.hasNumericValue() ? (int) select.getUnsignedValue() : -1;
- if (selectValue >= outputs.length)
- selectValue = -1;
-
- if (selected != selectValue && selected != -1)
- outputs[selected].clearSignals();
-
- selected = selectValue;
-
- if (selectValue != -1)
- outputs[selectValue].feedSignals(in.getValues());
- }
-
- @Override
- public List<ReadEnd> getAllInputs()
- {
- return List.of(in, select);
- }
-
- @Override
- public List<ReadWriteEnd> getAllOutputs()
- {
- return List.of(outputs);
- }
-}
+++ /dev/null
-package era.mi.logic.components;
-
-import java.util.List;
-
-import era.mi.logic.timeline.Timeline;
-import era.mi.logic.types.Bit;
-import era.mi.logic.wires.Wire.ReadEnd;
-import era.mi.logic.wires.Wire.ReadWriteEnd;
-
-/**
- * This class models a simple on/off (ONE/ZERO) switch for user interaction.
- *
- * @author Christian Femers
- *
- */
-public class ManualSwitch extends Component
-{
- private ReadWriteEnd output;
- private boolean isOn;
-
- public ManualSwitch(Timeline timeline, ReadWriteEnd output)
- {
- super(timeline);
- if (output.length() != 1)
- throw new IllegalArgumentException("Switch output can be only a single wire");
- this.output = output;
- }
-
- public void switchOn()
- {
- setState(true);
- }
-
- public void switchOff()
- {
- setState(false);
- }
-
- public void toggle()
- {
- setState(!isOn);
- }
-
- public void setState(boolean isOn)
- {
- if (this.isOn == isOn)
- return;
- this.isOn = isOn;
- output.feedSignals(getValue());
- }
-
- public boolean isOn()
- {
- return isOn;
- }
-
- public Bit getValue()
- {
- return isOn ? Bit.ONE : Bit.ZERO;
- }
-
- @Override
- public List<ReadEnd> getAllInputs()
- {
- return List.of();
- }
-
- @Override
- public List<ReadWriteEnd> getAllOutputs()
- {
- return List.of(output);
- }
-
-}
+++ /dev/null
-package era.mi.logic.components;
-
-import java.util.List;
-
-import era.mi.logic.timeline.Timeline;
-import era.mi.logic.types.BitVector;
-import era.mi.logic.wires.Wire;
-import era.mi.logic.wires.Wire.ReadEnd;
-import era.mi.logic.wires.Wire.ReadWriteEnd;
-import era.mi.logic.wires.WireObserver;
-
-public class Merger extends Component implements WireObserver
-{
- private ReadWriteEnd out;
- private ReadEnd[] inputs;
- private int[] beginningIndex;
-
- /**
- *
- * @param union The output of merging n {@link Wire}s into one. Must have length = a1.length() + a2.length() + ... + an.length().
- * @param inputs The inputs to be merged into the union
- */
- public Merger(Timeline timeline, ReadWriteEnd union, ReadEnd... inputs)
- {
- super(timeline);
- this.inputs = inputs;
- this.out = union;
- this.beginningIndex = new int[inputs.length];
-
- int length = 0;
- for (int i = 0; i < inputs.length; i++)
- {
- beginningIndex[i] = length;
- length += inputs[i].length();
- inputs[i].addObserver(this);
- }
-
- if (length != union.length())
- throw new IllegalArgumentException(
- "The output of merging n WireArrays into one must have length = a1.length() + a2.length() + ... + an.length().");
- }
-
- public ReadEnd getInput(int index)
- {
- return inputs[index];
- }
-
- public ReadEnd getUnion()
- {
- return out;
- }
-
- @Override
- public void update(ReadEnd initiator, BitVector oldValues)
- {
- int index = find(initiator);
- int beginning = beginningIndex[index];
- out.feedSignals(beginning, inputs[index].getValues());
- }
-
- private int find(ReadEnd r)
- {
- for (int i = 0; i < inputs.length; i++)
- if (inputs[i] == r)
- return i;
- return -1;
- }
-
- public ReadEnd[] getInputs()
- {
- return inputs.clone();
- }
-
- @Override
- public List<ReadEnd> getAllInputs()
- {
- return List.of(inputs);
- }
-
- @Override
- public List<ReadWriteEnd> getAllOutputs()
- {
- return List.of(out);
- }
-}
+++ /dev/null
-package era.mi.logic.components;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import era.mi.logic.timeline.Timeline;
-import era.mi.logic.wires.Wire;
-import era.mi.logic.wires.Wire.ReadEnd;
-import era.mi.logic.wires.Wire.ReadWriteEnd;
-
-/**
- * Models a multiplexer. Takes an arbitrary amount of input {@link Wire}s, one of which, as determined by select, is put through to the
- * output.
- *
- * @author Fabian Stemmler
- *
- */
-public class Mux extends BasicComponent
-{
- private ReadEnd select;
- private ReadWriteEnd out;
- private ReadEnd[] inputs;
- private final int outputSize;
-
- /**
- * Input {@link Wire}s and out must be of uniform length
- *
- * @param out Must be of uniform length with all inputs.
- * @param select Indexes the input array which is to be mapped to the output. Must have enough bits to index all inputs.
- * @param inputs One of these inputs is mapped to the output, depending on the select bits
- */
- public Mux(Timeline timeline, int processTime, ReadWriteEnd out, ReadEnd select, ReadEnd... inputs)
- {
- super(timeline, processTime);
- outputSize = out.length();
-
- this.inputs = inputs.clone();
- for (int i = 0; i < this.inputs.length; i++)
- {
- if (inputs[i].length() != outputSize)
- throw new IllegalArgumentException("All MUX wire arrays must be of uniform length!");
- inputs[i].addObserver(this);
- }
-
- this.select = select;
- select.addObserver(this);
-
- int maxInputs = 1 << select.length();
- if (this.inputs.length > maxInputs)
- throw new IllegalArgumentException("There are more inputs (" + this.inputs.length + ") to the MUX than supported by "
- + select.length() + " select bits (" + maxInputs + ").");
-
- this.out = out;
- }
-
- public ReadEnd getOut()
- {
- return out;
- }
-
- public ReadEnd getSelect()
- {
- return select;
- }
-
- @Override
- public void compute()
- {
- int selectValue;
- if (!select.hasNumericValue() || (selectValue = (int) select.getUnsignedValue()) >= inputs.length)
- {
- out.clearSignals();
- return;
- }
-
- ReadEnd active = inputs[selectValue];
- out.feedSignals(active.getValues());
- }
-
- @Override
- public List<ReadEnd> getAllInputs()
- {
- ArrayList<ReadEnd> wires = new ArrayList<ReadEnd>(Arrays.asList(inputs));
- wires.add(select);
- return Collections.unmodifiableList(wires);
- }
-
- @Override
- public List<ReadWriteEnd> getAllOutputs()
- {
- return List.of(out);
- }
-}
+++ /dev/null
-package era.mi.logic.components;
-
-import java.util.List;
-
-import era.mi.logic.timeline.Timeline;
-import era.mi.logic.types.BitVector;
-import era.mi.logic.wires.Wire.ReadEnd;
-import era.mi.logic.wires.Wire.ReadWriteEnd;
-import era.mi.logic.wires.WireObserver;
-
-public class Splitter extends Component implements WireObserver
-{
- private ReadEnd input;
- private ReadWriteEnd[] outputs;
-
- public Splitter(Timeline timeline, ReadEnd input, ReadWriteEnd... outputs)
- {
- super(timeline);
- this.input = input;
- this.outputs = outputs;
- input.addObserver(this);
- int length = 0;
- for (ReadEnd out : outputs)
- length += out.length();
-
- if (input.length() != length)
- throw new IllegalArgumentException(
- "The input of splitting one into n WireArrays must have length = a1.length() + a2.length() + ... + an.length().");
- }
-
- protected void compute()
- {
- BitVector inputBits = input.getValues();
- int startIndex = 0;
- for (int i = 0; i < outputs.length; i++)
- {
- outputs[i].feedSignals(inputBits.subVector(startIndex, startIndex + outputs[i].length()));
- startIndex += outputs[i].length();
- }
- }
-
- @Override
- public void update(ReadEnd initiator, BitVector oldValues)
- {
- compute();
- }
-
- @Override
- public List<ReadEnd> getAllInputs()
- {
- return List.of(input);
- }
-
- @Override
- public List<ReadWriteEnd> getAllOutputs()
- {
- return List.of(outputs);
- }
-}
+++ /dev/null
-package era.mi.logic.components;
-
-import java.util.List;
-
-import era.mi.logic.timeline.Timeline;
-import era.mi.logic.types.Bit;
-import era.mi.logic.wires.Wire.ReadEnd;
-import era.mi.logic.wires.Wire.ReadWriteEnd;
-
-public class TriStateBuffer extends BasicComponent
-{
- ReadEnd in, enable;
- ReadWriteEnd out;
-
- public TriStateBuffer(Timeline timeline, int processTime, ReadEnd in, ReadWriteEnd out, ReadEnd enable)
- {
- super(timeline, processTime);
- if (in.length() != out.length())
- throw new IllegalArgumentException(
- "Tri-state output must have the same amount of bits as the input. Input: " + in.length() + " Output: " + out.length());
- if (enable.length() != 1)
- throw new IllegalArgumentException("Tri-state enable must have exactly one bit, not " + enable.length() + ".");
- this.in = in;
- in.addObserver(this);
- this.enable = enable;
- enable.addObserver(this);
- this.out = out;
- }
-
- @Override
- protected void compute()
- {
- if (enable.getValue() == Bit.ONE)
- out.feedSignals(in.getValues());
- else
- out.clearSignals();
- }
-
- @Override
- public List<ReadEnd> getAllInputs()
- {
- return List.of(in, enable);
- }
-
- @Override
- public List<ReadWriteEnd> getAllOutputs()
- {
- return List.of(out);
- }
-
-}
+++ /dev/null
-package era.mi.logic.components.gates;
-
-import era.mi.logic.timeline.Timeline;
-import era.mi.logic.types.BitVector.BitVectorMutator;
-import era.mi.logic.wires.Wire.ReadEnd;
-import era.mi.logic.wires.Wire.ReadWriteEnd;
-
-public class AndGate extends MultiInputGate
-{
- public AndGate(Timeline timeline, int processTime, ReadWriteEnd out, ReadEnd... in)
- {
- super(timeline, processTime, BitVectorMutator::and, out, in);
- }
-}
+++ /dev/null
-package era.mi.logic.components.gates;
-
-import java.util.List;
-
-import era.mi.logic.components.BasicComponent;
-import era.mi.logic.timeline.Timeline;
-import era.mi.logic.types.BitVector.BitVectorMutator;
-import era.mi.logic.types.MutationOperation;
-import era.mi.logic.wires.Wire.ReadEnd;
-import era.mi.logic.wires.Wire.ReadWriteEnd;
-
-public abstract class MultiInputGate extends BasicComponent
-{
- protected ReadEnd[] in;
- protected ReadWriteEnd out;
- protected final int length;
- protected MutationOperation op;
-
- protected MultiInputGate(Timeline timeline, int processTime, MutationOperation op, ReadWriteEnd out, ReadEnd... in)
- {
- super(timeline, processTime);
- this.op = op;
- length = out.length();
- this.in = in.clone();
- if (in.length < 1)
- throw new IllegalArgumentException(String.format("Cannot create gate with %d wires.", in.length));
- for (ReadEnd w : in)
- {
- if (w.length() != length)
- throw new IllegalArgumentException("All wires connected to the gate must be of uniform length.");
- w.addObserver(this);
- }
- this.out = out;
- }
-
- @Override
- public List<ReadEnd> getAllInputs()
- {
- return List.of(in);
- }
-
- @Override
- public List<ReadWriteEnd> getAllOutputs()
- {
- return List.of(out);
- }
-
- @Override
- protected void compute()
- {
- BitVectorMutator mutator = BitVectorMutator.empty();
- for (ReadEnd w : in)
- op.apply(mutator, w.getValues());
- out.feedSignals(mutator.get());
- }
-}
+++ /dev/null
-package era.mi.logic.components.gates;
-
-import java.util.List;
-
-import era.mi.logic.components.BasicComponent;
-import era.mi.logic.timeline.Timeline;
-import era.mi.logic.wires.Wire.ReadEnd;
-import era.mi.logic.wires.Wire.ReadWriteEnd;
-
-public class NotGate extends BasicComponent
-{
- private ReadEnd in;
- private ReadWriteEnd out;
-
- public NotGate(Timeline timeline, int processTime, ReadEnd in, ReadWriteEnd out)
- {
- super(timeline, processTime);
- this.in = in;
- in.addObserver(this);
- this.out = out;
- }
-
- @Override
- protected void compute()
- {
- out.feedSignals(in.getValues().not());
- }
-
- public ReadEnd getIn()
- {
- return in;
- }
-
- public ReadEnd getOut()
- {
- return out;
- }
-
- @Override
- public List<ReadEnd> getAllInputs()
- {
- return List.of(in);
- }
-
- @Override
- public List<ReadWriteEnd> getAllOutputs()
- {
- return List.of(out);
- }
-}
+++ /dev/null
-package era.mi.logic.components.gates;
-
-import era.mi.logic.timeline.Timeline;
-import era.mi.logic.types.BitVector.BitVectorMutator;
-import era.mi.logic.wires.Wire.ReadEnd;
-import era.mi.logic.wires.Wire.ReadWriteEnd;
-
-public class OrGate extends MultiInputGate
-{
- public OrGate(Timeline timeline, int processTime, ReadWriteEnd out, ReadEnd... in)
- {
- super(timeline, processTime, BitVectorMutator::or, out, in);
- }
-}
+++ /dev/null
-package era.mi.logic.components.gates;
-
-import era.mi.logic.timeline.Timeline;
-import era.mi.logic.types.BitVector.BitVectorMutator;
-import era.mi.logic.wires.Wire.ReadEnd;
-import era.mi.logic.wires.Wire.ReadWriteEnd;
-
-/**
- * Outputs 1 when the number of 1 inputs is odd.
- *
- * @author Fabian Stemmler
- */
-public class XorGate extends MultiInputGate
-{
- public XorGate(Timeline timeline, int processTime, ReadWriteEnd out, ReadEnd... in)
- {
- super(timeline, processTime, BitVectorMutator::xor, out, in);
- }
-
-}
+++ /dev/null
-package era.mi.logic.tests;
-
-import static org.junit.Assert.assertTrue;
-import static org.junit.jupiter.api.Assertions.assertArrayEquals;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.fail;
-
-import java.util.function.LongConsumer;
-
-import org.junit.jupiter.api.Test;
-
-import era.mi.logic.components.Connector;
-import era.mi.logic.components.Demux;
-import era.mi.logic.components.Merger;
-import era.mi.logic.components.Mux;
-import era.mi.logic.components.Splitter;
-import era.mi.logic.components.TriStateBuffer;
-import era.mi.logic.components.gates.AndGate;
-import era.mi.logic.components.gates.NotGate;
-import era.mi.logic.components.gates.OrGate;
-import era.mi.logic.components.gates.XorGate;
-import era.mi.logic.timeline.Timeline;
-import era.mi.logic.types.Bit;
-import era.mi.logic.types.BitVector;
-import era.mi.logic.wires.Wire;
-import era.mi.logic.wires.Wire.ReadEnd;
-import era.mi.logic.wires.Wire.ReadWriteEnd;
-
-class ComponentTest
-{
- private Timeline t = new Timeline(11);
-
- @Test
- void circuitExampleTest()
- {
- Wire a = new Wire(t, 1, 1), b = new Wire(t, 1, 1), c = new Wire(t, 1, 10), d = new Wire(t, 2, 1), e = new Wire(t, 1, 1),
- f = new Wire(t, 1, 1), g = new Wire(t, 1, 1), h = new Wire(t, 2, 1), i = new Wire(t, 2, 1), j = new Wire(t, 1, 1),
- k = new Wire(t, 1, 1);
- new AndGate(t, 1, f.createReadWriteEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd());
- new NotGate(t, 1, f.createReadOnlyEnd(), g.createReadWriteEnd());
- new Merger(t, h.createReadWriteEnd(), c.createReadOnlyEnd(), g.createReadOnlyEnd());
- new Mux(t, 1, i.createReadWriteEnd(), e.createReadOnlyEnd(), h.createReadOnlyEnd(), d.createReadOnlyEnd());
- new Splitter(t, i.createReadOnlyEnd(), k.createReadWriteEnd(), j.createReadWriteEnd());
-
- a.createReadWriteEnd().feedSignals(Bit.ZERO);
- b.createReadWriteEnd().feedSignals(Bit.ONE);
- c.createReadWriteEnd().feedSignals(Bit.ZERO);
- d.createReadWriteEnd().feedSignals(Bit.ONE, Bit.ONE);
- e.createReadWriteEnd().feedSignals(Bit.ZERO);
-
- t.executeAll();
-
- assertEquals(Bit.ONE, j.getValue());
- assertEquals(Bit.ZERO, k.getValue());
- }
-
- @Test
- void splitterTest()
- {
- t.reset();
- Wire a = new Wire(t, 3, 1), b = new Wire(t, 2, 1), c = new Wire(t, 3, 1), in = new Wire(t, 8, 1);
- in.createReadWriteEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE);
- new Splitter(t, in.createReadOnlyEnd(), a.createReadWriteEnd(), b.createReadWriteEnd(), c.createReadWriteEnd());
-
- t.executeAll();
-
- assertBitArrayEquals(a.getValues(), Bit.ZERO, Bit.ONE, Bit.ZERO);
- assertBitArrayEquals(b.getValues(), Bit.ONE, Bit.ZERO);
- assertBitArrayEquals(c.getValues(), Bit.ONE, Bit.ZERO, Bit.ONE);
- }
-
- @Test
- void mergerTest()
- {
- t.reset();
- Wire a = new Wire(t, 3, 1), b = new Wire(t, 2, 1), c = new Wire(t, 3, 1), out = new Wire(t, 8, 1);
- a.createReadWriteEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ZERO);
- b.createReadWriteEnd().feedSignals(Bit.ONE, Bit.ZERO);
- c.createReadWriteEnd().feedSignals(Bit.ONE, Bit.ZERO, Bit.ONE);
-
- new Merger(t, out.createReadWriteEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd(), c.createReadOnlyEnd());
-
- t.executeAll();
-
- assertBitArrayEquals(out.getValues(), Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE);
- }
-
- @Test
- void triStateBufferTest()
- {
- Wire a = new Wire(t, 1, 1), b = new Wire(t, 1, 1), en = new Wire(t, 1, 1), notEn = new Wire(t, 1, 1);
- new NotGate(t, 1, en.createReadOnlyEnd(), notEn.createReadWriteEnd());
- new TriStateBuffer(t, 1, a.createReadOnlyEnd(), b.createReadWriteEnd(), en.createReadOnlyEnd());
- new TriStateBuffer(t, 1, b.createReadOnlyEnd(), a.createReadWriteEnd(), notEn.createReadOnlyEnd());
-
- ReadWriteEnd enI = en.createReadWriteEnd(), aI = a.createReadWriteEnd(), bI = b.createReadWriteEnd();
- enI.feedSignals(Bit.ONE);
- aI.feedSignals(Bit.ONE);
- bI.feedSignals(Bit.Z);
-
- t.executeAll();
-
- assertEquals(Bit.ONE, b.getValue());
-
- bI.feedSignals(Bit.ZERO);
-
- t.executeAll();
-
- assertEquals(Bit.X, b.getValue());
- assertEquals(Bit.ONE, a.getValue());
-
- aI.clearSignals();
- enI.feedSignals(Bit.ZERO);
-
- t.executeAll();
-
- assertEquals(Bit.ZERO, a.getValue());
-
- }
-
- @Test
- void muxTest()
- {
- t.reset();
- Wire a = new Wire(t, 4, 3), b = new Wire(t, 4, 6), c = new Wire(t, 4, 4), select = new Wire(t, 2, 5), out = new Wire(t, 4, 1);
- ReadWriteEnd selectIn = select.createReadWriteEnd();
-
- selectIn.feedSignals(Bit.ZERO, Bit.ZERO);
- a.createReadWriteEnd().feedSignals(Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO);
- c.createReadWriteEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE);
-
- new Mux(t, 1, out.createReadWriteEnd(), select.createReadOnlyEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd(),
- c.createReadOnlyEnd());
- t.executeAll();
-
- assertBitArrayEquals(out.getValues(), Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO);
- selectIn.feedSignals(Bit.ZERO, Bit.ONE);
- t.executeAll();
-
- assertBitArrayEquals(out.getValues(), Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE);
-
- selectIn.feedSignals(Bit.ONE, Bit.ONE);
- t.executeAll();
-
- assertBitArrayEquals(out.getValues(), Bit.Z, Bit.Z, Bit.Z, Bit.Z);
-
- }
-
- @Test
- void demuxTest()
- {
- t.reset();
- Wire a = new Wire(t, 4, 3), b = new Wire(t, 4, 6), c = new Wire(t, 4, 4), select = new Wire(t, 2, 5), in = new Wire(t, 4, 1);
- ReadWriteEnd selectIn = select.createReadWriteEnd();
-
- selectIn.feedSignals(Bit.ZERO, Bit.ZERO);
- in.createReadWriteEnd().feedSignals(Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO);
-
- new Demux(t, 1, in.createReadOnlyEnd(), select.createReadOnlyEnd(), a.createReadWriteEnd(), b.createReadWriteEnd(),
- c.createReadWriteEnd());
- t.executeAll();
-
- assertBitArrayEquals(a.getValues(), Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO);
- assertBitArrayEquals(b.getValues(), Bit.U, Bit.U, Bit.U, Bit.U);
- assertBitArrayEquals(c.getValues(), Bit.U, Bit.U, Bit.U, Bit.U);
- selectIn.feedSignals(Bit.ZERO, Bit.ONE);
- t.executeAll();
-
- assertBitArrayEquals(a.getValues(), Bit.Z, Bit.Z, Bit.Z, Bit.Z);
- assertBitArrayEquals(b.getValues(), Bit.U, Bit.U, Bit.U, Bit.U);
- assertBitArrayEquals(c.getValues(), Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO);
-
- selectIn.feedSignals(Bit.ONE, Bit.ONE);
- t.executeAll();
-
- assertBitArrayEquals(a.getValues(), Bit.Z, Bit.Z, Bit.Z, Bit.Z);
- assertBitArrayEquals(b.getValues(), Bit.U, Bit.U, Bit.U, Bit.U);
- assertBitArrayEquals(c.getValues(), Bit.Z, Bit.Z, Bit.Z, Bit.Z);
-
- }
-
- @Test
- void andTest()
- {
- t.reset();
- Wire a = new Wire(t, 4, 1), b = new Wire(t, 4, 3), c = new Wire(t, 4, 1);
- new AndGate(t, 1, c.createReadWriteEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd());
- a.createReadWriteEnd().feedSignals(Bit.ONE, Bit.ONE, Bit.ZERO, Bit.ZERO);
- b.createReadWriteEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE);
-
- t.executeAll();
-
- assertBitArrayEquals(c.getValues(), Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ZERO);
- }
-
- @Test
- void orTest()
- {
- t.reset();
- Wire a = new Wire(t, 4, 1), b = new Wire(t, 4, 3), c = new Wire(t, 4, 1);
- new OrGate(t, 1, c.createReadWriteEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd());
- a.createReadWriteEnd().feedSignals(Bit.ONE, Bit.ONE, Bit.ZERO, Bit.ZERO);
- b.createReadWriteEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE);
-
- t.executeAll();
-
- assertBitArrayEquals(c.getValues(), Bit.ONE, Bit.ONE, Bit.ZERO, Bit.ONE);
- }
-
- @Test
- void xorTest()
- {
- t.reset();
- Wire a = new Wire(t, 3, 1), b = new Wire(t, 3, 2), c = new Wire(t, 3, 1), d = new Wire(t, 3, 1);
- new XorGate(t, 1, d.createReadWriteEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd(), c.createReadOnlyEnd());
- a.createReadWriteEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ONE);
- b.createReadWriteEnd().feedSignals(Bit.ONE, Bit.ZERO, Bit.ONE);
- c.createReadWriteEnd().feedSignals(Bit.ONE, Bit.ZERO, Bit.ONE);
-
- t.executeAll();
-
- assertBitArrayEquals(d.getValues(), Bit.ZERO, Bit.ONE, Bit.ONE);
- }
-
- @Test
- void notTest()
- {
- t.reset();
- Wire a = new Wire(t, 3, 1), b = new Wire(t, 3, 2);
- new NotGate(t, 1, a.createReadOnlyEnd(), b.createReadWriteEnd());
- a.createReadWriteEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ONE);
-
- t.executeAll();
-
- assertBitArrayEquals(b.getValues(), Bit.ONE, Bit.ZERO, Bit.ZERO);
- }
-
- @Test
- void rsLatchCircuitTest()
- {
- t.reset();
- Wire r = new Wire(t, 1, 1), s = new Wire(t, 1, 1), t1 = new Wire(t, 1, 15), t2 = new Wire(t, 1, 1), q = new Wire(t, 1, 1),
- nq = new Wire(t, 1, 1);
-
- new OrGate(t, 1, t2.createReadWriteEnd(), r.createReadOnlyEnd(), nq.createReadOnlyEnd());
- new OrGate(t, 1, t1.createReadWriteEnd(), s.createReadOnlyEnd(), q.createReadOnlyEnd());
- new NotGate(t, 1, t2.createReadOnlyEnd(), q.createReadWriteEnd());
- new NotGate(t, 1, t1.createReadOnlyEnd(), nq.createReadWriteEnd());
-
- ReadWriteEnd sIn = s.createReadWriteEnd(), rIn = r.createReadWriteEnd();
-
- sIn.feedSignals(Bit.ONE);
- rIn.feedSignals(Bit.ZERO);
-
- t.executeAll();
-
- assertEquals(Bit.ONE, q.getValue());
- assertEquals(Bit.ZERO, nq.getValue());
-
- sIn.feedSignals(Bit.ZERO);
-
- t.executeAll();
- assertEquals(Bit.ONE, q.getValue());
- assertEquals(Bit.ZERO, nq.getValue());
-
- rIn.feedSignals(Bit.ONE);
-
- t.executeAll();
-
- assertEquals(Bit.ZERO, q.getValue());
- assertEquals(Bit.ONE, nq.getValue());
- }
-
- @Test
- void numericValueTest()
- {
- t.reset();
-
- Wire a = new Wire(t, 4, 1);
- a.createReadWriteEnd().feedSignals(Bit.ONE, Bit.ONE, Bit.ONE, Bit.ONE);
-
- t.executeAll();
-
- assertEquals(15, a.getUnsignedValue());
- assertEquals(-1, a.getSignedValue());
- }
-
- boolean flag = false;
-
- @Test
- void simpleTimelineTest()
- {
- Timeline t = new Timeline(3);
- flag = false;
- t.addEvent((e) ->
- {
- if (!flag)
- fail();
- flag = false;
- }, 15);
- t.addEvent((e) ->
- {
- if (flag)
- fail();
- flag = true;
- }, 10);
- t.addEvent((e) ->
- {
- if (flag)
- fail();
- flag = true;
- }, 20);
- t.addEvent((e) ->
- {
- fail("Only supposed to execute until timestamp 20, not 25");
- }, 25);
-
- t.executeUntil(t.laterThan(20), 100);
-
- if (!flag)
- fail();
- }
-
- @Test
- void multipleInputs()
- {
- t.reset();
- Wire w = new Wire(t, 2, 1);
- ReadWriteEnd wI1 = w.createReadWriteEnd(), wI2 = w.createReadWriteEnd();
- wI1.feedSignals(Bit.ONE, Bit.Z);
- wI2.feedSignals(Bit.Z, Bit.X);
- t.executeAll();
- assertBitArrayEquals(w.getValues(), Bit.ONE, Bit.X);
-
- wI2.feedSignals(Bit.ZERO, Bit.Z);
- t.executeAll();
- assertBitArrayEquals(w.getValues(), Bit.X, Bit.Z);
-
- wI2.feedSignals(Bit.Z, Bit.Z);
- t.executeAll();
- assertBitArrayEquals(w.getValues(), Bit.ONE, Bit.Z);
-
- wI2.feedSignals(Bit.ONE, Bit.Z);
- ReadEnd rE = w.createReadOnlyEnd();
- rE.addObserver((i, oldValues) -> fail("WireEnd notified observer, although value did not change."));
- t.executeAll();
- rE.close();
- wI1.feedSignals(Bit.X, Bit.X);
- t.executeAll();
- wI1.addObserver((i, oldValues) -> fail("WireEnd notified observer, although it was closed."));
- wI1.close();
- assertBitArrayEquals(w.getValues(), Bit.ONE, Bit.Z);
- }
-
- @Test
- void wireConnections()
- {
- // Nur ein Experiment, was über mehrere 'passive' Bausteine hinweg passieren würde
-
- t.reset();
-
- Wire a = new Wire(t, 1, 2);
- Wire b = new Wire(t, 1, 2);
- Wire c = new Wire(t, 1, 2);
- ReadWriteEnd aI = a.createReadWriteEnd();
- ReadWriteEnd bI = b.createReadWriteEnd();
- ReadWriteEnd cI = c.createReadWriteEnd();
-
- TestBitDisplay test = new TestBitDisplay(t, c.createReadOnlyEnd());
- TestBitDisplay test2 = new TestBitDisplay(t, a.createReadOnlyEnd());
- LongConsumer print = time -> System.out.format("Time %2d\n a: %s\n b: %s\n c: %s\n", time, a, b, c);
-
- cI.feedSignals(Bit.ONE);
- test.assertAfterSimulationIs(print, Bit.ONE);
-
- cI.feedSignals(Bit.X);
- test.assertAfterSimulationIs(print, Bit.X);
-
- cI.feedSignals(Bit.X);
- cI.feedSignals(Bit.Z);
- test.assertAfterSimulationIs(print, Bit.Z);
-
- new Connector(t, b.createReadWriteEnd(), c.createReadWriteEnd()).connect();
- test.assertAfterSimulationIs(print, Bit.Z);
- System.err.println("ONE");
- bI.feedSignals(Bit.ONE);
- test.assertAfterSimulationIs(print, Bit.ONE);
- System.err.println("ZERO");
- bI.feedSignals(Bit.ZERO);
- test.assertAfterSimulationIs(print, Bit.ZERO);
- System.err.println("Z");
- bI.feedSignals(Bit.Z);
- test.assertAfterSimulationIs(print, Bit.Z);
-
- new Connector(t, a.createReadWriteEnd(), b.createReadWriteEnd()).connect();
- System.err.println("Z 2");
- aI.feedSignals(Bit.Z);
- test.assertAfterSimulationIs(print, Bit.Z);
- test2.assertAfterSimulationIs(Bit.Z);
- System.err.println("ONE 2");
- aI.feedSignals(Bit.ONE);
- test.assertAfterSimulationIs(print, Bit.ONE);
- test2.assertAfterSimulationIs(Bit.ONE);
- System.err.println("ZERO 2");
- aI.feedSignals(Bit.ZERO);
- test.assertAfterSimulationIs(print, Bit.ZERO);
- test2.assertAfterSimulationIs(Bit.ZERO);
- System.err.println("Z 2 II");
- aI.feedSignals(Bit.Z);
- test.assertAfterSimulationIs(print, Bit.Z);
- test2.assertAfterSimulationIs(Bit.Z);
-
- System.err.println("No Conflict yet");
- bI.feedSignals(Bit.ONE);
- test.assertAfterSimulationIs(print, Bit.ONE);
- test2.assertAfterSimulationIs(Bit.ONE);
- aI.feedSignals(Bit.ONE);
- test.assertAfterSimulationIs(print, Bit.ONE);
- test2.assertAfterSimulationIs(Bit.ONE);
- System.err.println("Conflict");
- aI.feedSignals(Bit.ZERO);
- test.assertAfterSimulationIs(print, Bit.X);
- test2.assertAfterSimulationIs(Bit.X);
- aI.feedSignals(Bit.ONE);
- test.assertAfterSimulationIs(print, Bit.ONE);
- test2.assertAfterSimulationIs(Bit.ONE);
- }
-
- private static void assertBitArrayEquals(BitVector actual, Bit... expected)
- {
- assertArrayEquals(expected, actual.getBits());
- }
-}
+++ /dev/null
-package era.mi.logic.tests;
-
-import java.awt.Color;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.Rectangle;
-import java.awt.RenderingHints;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseListener;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import javax.swing.JFrame;
-import javax.swing.JPanel;
-import javax.swing.WindowConstants;
-
-import era.mi.logic.components.ManualSwitch;
-import era.mi.logic.components.gates.NotGate;
-import era.mi.logic.components.gates.OrGate;
-import era.mi.logic.timeline.Timeline;
-import era.mi.logic.timeline.Timeline.ExecutionResult;
-import era.mi.logic.wires.Wire;
-
-public class GUITest extends JPanel
-{
-
- private static final long serialVersionUID = 1L;
-
- private static final int WIRE_DELAY = 40;
- private static final int OR_DELAY = 100;
- private static final int NOT_DELAY = 100;
-
- private Timeline t = new Timeline(11);
-
- Wire r = new Wire(t, 1, WIRE_DELAY);
- Wire s = new Wire(t, 1, WIRE_DELAY);
- Wire t1 = new Wire(t, 1, WIRE_DELAY);
- Wire t2 = new Wire(t, 1, WIRE_DELAY);
- Wire q = new Wire(t, 1, WIRE_DELAY);
- Wire nq = new Wire(t, 1, WIRE_DELAY);
-
- ManualSwitch rIn = new ManualSwitch(t, r.createReadWriteEnd());
- ManualSwitch sIn = new ManualSwitch(t, s.createReadWriteEnd());
-
- OrGate or1 = new OrGate(t, OR_DELAY, t2.createReadWriteEnd(), r.createReadOnlyEnd(), nq.createReadOnlyEnd());
- OrGate or2 = new OrGate(t, OR_DELAY, t1.createReadWriteEnd(), s.createReadOnlyEnd(), q.createReadOnlyEnd());
- NotGate not1 = new NotGate(t, NOT_DELAY, t2.createReadOnlyEnd(), q.createReadWriteEnd());
- NotGate not2 = new NotGate(t, NOT_DELAY, t1.createReadOnlyEnd(), nq.createReadWriteEnd());
-
- Map<ManualSwitch, Rectangle> switchMap = new HashMap<>();
-
- int height;
- int width;
- boolean sizeChanged;
-
- public GUITest()
- {
- addMouseListener(new MouseListener()
- {
-
- @Override
- public void mouseReleased(MouseEvent e)
- {
- for (Entry<ManualSwitch, Rectangle> dim : switchMap.entrySet())
- {
- if (dim.getValue().contains(e.getPoint()))
- {
- dim.getKey().switchOff();
- repaint();
- }
- }
- }
-
- @Override
- public void mousePressed(MouseEvent e)
- {
- for (Entry<ManualSwitch, Rectangle> dim : switchMap.entrySet())
- {
- if (dim.getValue().contains(e.getPoint()))
- {
- dim.getKey().switchOn();
- repaint();
- }
- }
- }
-
- @Override
- public void mouseExited(MouseEvent e)
- {
- // none
- }
-
- @Override
- public void mouseEntered(MouseEvent e)
- {
- // none
- }
-
- @Override
- public void mouseClicked(MouseEvent e)
- {
- // If you want toggle buttons, use this code instead
-// for (Entry<ManualSwitch, Rectangle> dim : switchMap.entrySet()) {
-// if (dim.getValue().contains(e.getPoint())) {
-// dim.getKey().toggle();
-// repaint();
-// }
-// }
- }
- });
- }
-
- public Timeline getTimeline()
- {
- return t;
- };
-
- @Override
- public void paint(Graphics some_g)
- {
- super.paint(some_g);
- Graphics2D g = ((Graphics2D) some_g);
- g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_GASP);
- g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
- g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
-
- checkSizeChange();
- adaptFont(g);
-
- drawWire(g, r, "r", 2, 9, 4, 9);
-
- drawWire(g, s, "s", 2, 3, 4, 3);
-
- drawWire(g, t2, "t2", 5, 8.5, 6, 8.5);
-
- drawWire(g, t1, "t1", 5, 3.5, 6, 3.5);
-
- drawWire(g, q, "q", 7, 8.5, 9, 8.5);
-
- drawWire(g, nq, "nq", 7, 3.5, 9, 3.5);
-
- drawWire(g, q, "", 7.5, 8.5, 7.5, 7.5);
- drawWire(g, q, "", 7.5, 7.5, 3, 4.5);
- drawWire(g, q, "", 3, 4.5, 3, 4);
- drawWire(g, q, "q", 3, 4, 4, 4);
-
- drawWire(g, nq, "", 7.5, 3.5, 7.5, 4.5);
- drawWire(g, nq, "", 7.5, 4.5, 3, 7.5);
- drawWire(g, nq, "", 3, 7.5, 3, 8);
- drawWire(g, nq, "nq", 3, 8, 4, 8);
-
- drawSquare(g, 4, 8, "OR");
- drawSquare(g, 4, 3, "OR");
-
- drawSquare(g, 6, 8, "NOT");
- drawSquare(g, 6, 3, "NOT");
-
- drawSwitch(g, rIn, "Switch R", 0.5, 8.25, 2, 9.75);
- drawSwitch(g, sIn, "Switch S", 0.5, 2.25, 2, 3.75);
-
- drawString(g, "Hint: drag the cursor out of the pressed switch to keep it's state", 5, 0, 0.0, 1.0);
- }
-
- private void checkSizeChange()
- {
- sizeChanged = height != getHeight() || width != getWidth();
- if (sizeChanged)
- {
- height = getHeight();
- width = getWidth();
- }
- }
-
- private void adaptFont(Graphics g)
- {
- g.setFont(g.getFont().deriveFont(Math.min(height, width) / 40f));
- }
-
- private void drawString(Graphics g, String s, int x, int y, double anchorX, double anchorY)
- {
- int h = g.getFontMetrics().getAscent();
- int w = g.getFontMetrics().stringWidth(s);
- g.drawString(s, x - (int) (w * anchorX), y + (int) (h * anchorY));
- }
-
- private void drawWire(Graphics g, Wire wa, String name, double x1, double y1, double x2, double y2)
- {
- setTo(g, wa);
- g.drawLine(gX(x1), gY(y1), gX(x2), gY(y2));
- drawString(g, name, (gX(x1) + gX(x2)) / 2, (gY(y1) + gY(y2)) / 2 - 5, 0, 0);
- }
-
- private void drawSquare(Graphics g, int posX, int posY, String text)
- {
- int x1 = gX(posX) - 5;
- int x2 = gX(posX + 1) + 5;
- int y1 = gY(posY) - 5;
- int y2 = gY(posY + 1) + 5;
-
- g.setColor(Color.WHITE);
- g.fillRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
- setBlack(g);
- g.drawRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
- drawString(g, text, (x1 + x2) / 2, (y1 + y2) / 2, 0.5, 0.5);
-
- }
-
- private void drawSwitch(Graphics g, ManualSwitch ms, String text, double posX1, double posY1, double posX2, double posY2)
- {
- int x1 = gX(posX1) - 5;
- int x2 = gX(posX2) + 5;
- int y1 = gY(posY1) - 5;
- int y2 = gY(posY2) + 5;
-
- if (sizeChanged)
- {
- Rectangle r = new Rectangle(x1, y1, x2 - x1, y2 - y1);
- switchMap.put(ms, r);
- }
-
- g.setColor(ms.isOn() ? Color.getHSBColor(.3f, .5f, 1f) : Color.WHITE);
- g.fillRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
- setBlack(g);
- g.drawRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
- drawString(g, text, (x1 + x2) / 2, (y1 + y2) / 2, 0.5, 0.5);
- }
-
- private static void setBlack(Graphics g)
- {
- g.setColor(Color.BLACK);
- }
-
- private static void setTo(Graphics g, Wire wa)
- {
- switch (wa.getValue())
- {
- case ONE:
- g.setColor(Color.GREEN);
- break;
- case X:
- g.setColor(Color.RED);
- break;
- case Z:
- g.setColor(Color.DARK_GRAY);
- break;
- case ZERO:
- g.setColor(Color.BLACK);
- break;
- case U:
- g.setColor(Color.MAGENTA);
- break;
- default:
- throw new IllegalArgumentException();
- }
- }
-
- private int gY(double pos)
- {
- return (int) (pos * height / 11);
- }
-
- private int gX(double pos)
- {
- return (int) (pos * width / 11) + 50;
- }
-
- public static void main(String[] args)
- {
- JFrame f = new JFrame("Test circuit 1.0.0");
- GUITest gt = new GUITest();
- f.add(gt);
- f.setSize(800, 600);
- f.setLocation(500, 400);
- f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
- f.setVisible(true);
-
- long begin = System.currentTimeMillis();
-
- long lastFrame = begin;
- long updateT = 16;
-
- while (f.isVisible())
- {
- ExecutionResult er = gt.getTimeline().executeUntil(gt.getTimeline().laterThan((lastFrame - begin) * 3), lastFrame + 14);
-// if (t.hasNext())
-// t.executeNext();
- if (er != ExecutionResult.NOTHING_DONE)
- gt.repaint(12);
- try
- {
- Thread.sleep(Math.max(updateT - System.currentTimeMillis() + lastFrame, 0));
- }
- catch (Exception e)
- {
- e.printStackTrace();
- }
- lastFrame = System.currentTimeMillis();
- }
- }
-}
+++ /dev/null
-package era.mi.logic.tests;
-
-import static org.junit.jupiter.api.Assertions.assertArrayEquals;
-
-import java.util.function.LongConsumer;
-
-import era.mi.logic.components.BitDisplay;
-import era.mi.logic.timeline.Timeline;
-import era.mi.logic.types.Bit;
-import era.mi.logic.wires.Wire.ReadEnd;
-
-public final class TestBitDisplay extends BitDisplay
-{
-
- public TestBitDisplay(Timeline timeline, ReadEnd in)
- {
- super(timeline, in);
- }
-
- public void assertDisplays(Bit... expected)
- {
- assertArrayEquals(expected, getDisplayedValue().getBits());
- }
-
- public void assertAfterSimulationIs(Bit... expected)
- {
- timeline.executeAll();
- assertDisplays(expected);
- }
-
- public void assertAfterSimulationIs(LongConsumer r, Bit... expected)
- {
- while (timeline.hasNext())
- {
- timeline.executeNext();
- r.accept(timeline.getSimulationTime());
- }
- assertDisplays(expected);
- }
-
- @Override
- protected void compute()
- {
- super.compute();
- System.out.println("update: value is " + getDisplayedValue());
- }
-}
+++ /dev/null
-package era.mi.logic.timeline;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.PriorityQueue;
-import java.util.function.BooleanSupplier;
-import java.util.function.Consumer;
-import java.util.function.LongSupplier;
-
-/**
- * Orders Events by the time they are due to be executed. Can execute Events individually.
- *
- * @author Fabian Stemmler
- *
- */
-public class Timeline
-{
- private PriorityQueue<InnerEvent> events;
- private LongSupplier time;
- private long lastTimeUpdated = 0;
-
- private final List<Consumer<TimelineEvent>> eventAddedListener;
-
- public Timeline(int initCapacity)
- {
- events = new PriorityQueue<InnerEvent>(initCapacity);
-
- eventAddedListener = new ArrayList<>();
- time = () -> lastTimeUpdated;
- }
-
- /**
- * @param timestamp exclusive
- * @return true if the first event is later than the timestamp
- */
- public BooleanSupplier laterThan(long timestamp)
- {
- return () -> timeCmp(events.peek().getTiming(), timestamp) > 0;
- }
-
- public boolean hasNext()
- {
- return !events.isEmpty();
- }
-
- /**
- * Executes all events at the next timestamp, at which there are any
- */
- public void executeNext()
- {
- InnerEvent first = events.peek();
- if (first != null)
- executeUntil(laterThan(first.getTiming()), -1);
- }
-
- public void executeAll()
- {
- while (hasNext())
- executeNext();
- }
-
- /**
- * Executes all events until a given condition is met. The simulation process can be constrained by a real world timestamp.
- *
- * @param condition the condition until which the events are be processed
- * @param stopMillis the System.currentTimeMillis() when simulation definitely needs to stop. A value of -1 means no timeout.
- * @return State of the event execution
- * @formatter:off
- * <code>NOTHING_DONE</code> if the {@link Timeline} was already empty
- * <code>EXEC_OUT_OF_TIME</code> if the given maximum time was reached
- * <code>EXEC_UNTIL_CONDITION</code> if the condition was met
- * <code>EXEC_UNTIL_EMPTY</code> if events were executed until the {@link Timeline} was empty
- * @formatter:on
- * @author Christian Femers, Fabian Stemmler
- */
- public ExecutionResult executeUntil(BooleanSupplier condition, long stopMillis)
- {
- if (events.isEmpty())
- {
- lastTimeUpdated = getSimulationTime();
- return ExecutionResult.NOTHING_DONE;
- }
- int checkStop = 0;
- InnerEvent first = events.peek();
- while (hasNext() && !condition.getAsBoolean())
- {
- events.remove();
- lastTimeUpdated = first.getTiming();
- first.run();
- // Don't check after every run
- checkStop = (checkStop + 1) % 10;
- if (checkStop == 0 && System.currentTimeMillis() >= stopMillis)
- return ExecutionResult.EXEC_OUT_OF_TIME;
- first = events.peek();
- }
- lastTimeUpdated = getSimulationTime();
- return hasNext() ? ExecutionResult.EXEC_UNTIL_EMPTY : ExecutionResult.EXEC_UNTIL_CONDITION;
- }
-
- public void setTimeFunction(LongSupplier time)
- {
- this.time = time;
- }
-
- public long getSimulationTime()
- {
- return time.getAsLong();
- }
-
- public long nextEventTime()
- {
- if (!hasNext())
- return -1;
- return events.peek().getTiming();
- }
-
- public void reset()
- {
- events.clear();
- lastTimeUpdated = 0;
- }
-
- public void addEventAddedListener(Consumer<TimelineEvent> listener)
- {
- eventAddedListener.add(listener);
- }
-
- public void removeEventAddedListener(Consumer<TimelineEvent> listener)
- {
- eventAddedListener.remove(listener);
- }
-
- /**
- * Adds an Event to the {@link Timeline}
- *
- * @param function The {@link TimelineEventHandler} that will be executed, when the {@link InnerEvent} occurs on the timeline.
- * @param relativeTiming The amount of MI ticks in which the {@link InnerEvent} is called, starting from the current time.
- */
- public void addEvent(TimelineEventHandler function, int relativeTiming)
- {
- long timing = getSimulationTime() + relativeTiming;
- TimelineEvent event = new TimelineEvent(timing);
- events.add(new InnerEvent(function, event));
- eventAddedListener.forEach(l -> l.accept(event));
- }
-
- private class InnerEvent implements Runnable, Comparable<InnerEvent>
- {
- private final TimelineEventHandler function;
- private final TimelineEvent event;
-
- /**
- * Creates an {@link InnerEvent}
- *
- * @param function {@link TimelineEventHandler} to be executed when the {@link InnerEvent} occurs
- * @param timing Point in the MI simulation {@link Timeline}, at which the {@link InnerEvent} is executed;
- */
- InnerEvent(TimelineEventHandler function, TimelineEvent event)
- {
- this.function = function;
- this.event = event;
- }
-
- public long getTiming()
- {
- return event.getTiming();
- }
-
- @Override
- public void run()
- {
- function.handle(event);
- }
-
- @Override
- public String toString()
- {
- return event.toString();
- }
-
- @Override
- public int compareTo(InnerEvent o)
- {
- return timeCmp(getTiming(), o.getTiming());
- }
- }
-
- public static int timeCmp(long a, long b)
- {
- return Long.signum(a - b);
- }
-
- @Override
- public String toString()
- {
- return String.format("Simulation time: %s, Last update: %d, Events: %s", getSimulationTime(), lastTimeUpdated, events.toString());
- }
-
- public enum ExecutionResult
- {
- NOTHING_DONE, EXEC_UNTIL_EMPTY, EXEC_UNTIL_CONDITION, EXEC_OUT_OF_TIME
- }
-}
\ No newline at end of file
+++ /dev/null
-package era.mi.logic.timeline;
-
-/**
- * A class that stores all relevant information about an event in the {@link Timeline}. Currently, there is not much relevant information to
- * store.
- *
- * @author Fabian Stemmler
- *
- */
-public class TimelineEvent
-{
- private final long timing;
-
- TimelineEvent(long timing)
- {
- super();
- this.timing = timing;
- }
-
- public long getTiming()
- {
- return timing;
- }
-
- @Override
- public String toString()
- {
- return "timestamp: " + timing;
- }
-}
\ No newline at end of file
+++ /dev/null
-package era.mi.logic.timeline;
-
-@FunctionalInterface
-public interface TimelineEventHandler
-{
- public void handle(TimelineEvent e);
-}
\ No newline at end of file
+++ /dev/null
-package era.mi.logic.types;
-
-import java.util.Arrays;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * stdlogic according to IEEE 1164
- */
-public enum Bit implements StrictLogicType<Bit>
-{
- U("U"), X("X"), ZERO("0"), ONE("1"), Z("Z");
-
- private final String symbol;
-
- private Bit(String symbol)
- {
- this.symbol = symbol;
- }
-
- @Override
- public Bit and(Bit other)
- {
- return fromTable(AND_TABLE, this, other);
- }
-
- @Override
- public Bit or(Bit other)
- {
- return fromTable(OR_TABLE, this, other);
- }
-
- @Override
- public Bit xor(Bit other)
- {
- return fromTable(XOR_TABLE, this, other);
- }
-
- @Override
- public Bit not()
- {
- switch (this)
- {
- case U:
- return U;
- case ONE:
- return ZERO;
- case ZERO:
- return ONE;
- default:
- return X;
- }
- }
-
- public Bit[] makeArray(int length)
- {
- Bit[] bits = new Bit[length];
- Arrays.fill(bits, this);
- return bits;
- }
-
- public BitVector toVector(int length)
- {
- return BitVector.of(this, length);
- }
-
- @Override
- public Bit join(Bit other)
- {
- return fromTable(JOIN_TABLE, this, other);
- }
-
- @Override
- public String toString()
- {
- return getSymbol();
- }
-
- public String getSymbol()
- {
- return symbol;
- }
-
- public static Bit parse(String s)
- {
- Bit bit = SYMBOL_MAP.get(s);
- Objects.requireNonNull(bit, "No Bit found for symbol " + s);
- return bit;
- }
-
- public static Bit parse(String s, int symbolPosition)
- {
- return parse(s.substring(symbolPosition, symbolPosition + 1));
- }
-
- private static Bit fromTable(Bit[][] table, Bit a, Bit b)
- {
- return table[a.ordinal()][b.ordinal()];
- }
-
- static final Map<String, Bit> SYMBOL_MAP = Map.of(U.symbol, U, X.symbol, X, ZERO.symbol, ZERO, ONE.symbol, ONE, Z.symbol, Z);
-
- // @formatter:off
- private static final Bit[][] JOIN_TABLE =
- { { U, U, U, U, U },
- { U, X, X, X, X },
- { U, X, ZERO, X, ZERO },
- { U, X, X, ONE, ONE },
- { U, X, ZERO, ONE, Z } };
-
- private static final Bit[][] AND_TABLE =
- { { U, U, ZERO, U, U },
- { U, X, ZERO, X, X },
- { ZERO, ZERO, ZERO, ZERO, ZERO },
- { U, X, ZERO, ONE, X },
- { U, X, ZERO, X, X } };
-
- private static final Bit[][] OR_TABLE =
- { { U, U, U, ONE, U },
- { U, X, X, ONE, X },
- { U, X, ZERO, ONE, X },
- { ONE, ONE, ONE, ONE, ONE },
- { U, X, X, ONE, X } };
-
- private static final Bit[][] XOR_TABLE =
- { { U, U, U, U, U },
- { U, X, X, X, X },
- { U, X, ZERO, ONE, X },
- { U, X, ONE, ZERO, X },
- { U, X, X, X, X } };
- // @formatter:on
-}
\ No newline at end of file
+++ /dev/null
-package era.mi.logic.types;
-
-import static java.lang.String.format;
-
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.NoSuchElementException;
-import java.util.Objects;
-import java.util.RandomAccess;
-import java.util.function.BinaryOperator;
-import java.util.function.UnaryOperator;
-
-/**
- * Immutable class representing a {@link Bit}Vector
- *
- *
- * @author Christian Femers
- *
- */
-public final class BitVector implements StrictLogicType<BitVector>, Iterable<Bit>, RandomAccess
-{
- private final Bit[] bits;
-
- private BitVector(Bit[] bits)
- {
- this.bits = Objects.requireNonNull(bits);
- }
-
- public static BitVector of(Bit... bits)
- {
- return new BitVector(bits.clone());
- }
-
- public static BitVector of(Bit bit, int length)
- {
- return new BitVector(bit.makeArray(length));
- }
-
- public BitVectorMutator mutator()
- {
- return BitVectorMutator.of(this);
- }
-
- public Bit getBit(int bitIndex)
- {
- return bits[bitIndex];
- }
-
- public Bit[] getBits()
- {
- return bits.clone();
- }
-
- @Override
- public BitVector join(BitVector t)
- {
- checkCompatibility(t);
- return new BitVector(binOp(bits.clone(), t.bits, Bit::join));
- }
-
- @Override
- public BitVector and(BitVector t)
- {
- checkCompatibility(t);
- return new BitVector(binOp(bits.clone(), t.bits, Bit::and));
- }
-
- @Override
- public BitVector or(BitVector t)
- {
- checkCompatibility(t);
- return new BitVector(binOp(bits.clone(), t.bits, Bit::or));
- }
-
- @Override
- public BitVector xor(BitVector t)
- {
- checkCompatibility(t);
- return new BitVector(binOp(bits.clone(), t.bits, Bit::xor));
- }
-
- @Override
- public BitVector not()
- {
- return new BitVector(unOp(bits.clone(), Bit::not));
- }
-
- public int length()
- {
- return bits.length;
- }
-
- public BitVector concat(BitVector other)
- {
- Bit[] newBits = Arrays.copyOf(bits, length() + other.length());
- System.arraycopy(other.bits, 0, newBits, length(), other.length());
- return new BitVector(newBits);
- }
-
- public BitVector subVector(int start)
- {
- return new BitVector(Arrays.copyOfRange(bits, start, length()));
- }
-
- public BitVector subVector(int start, int end)
- {
- return new BitVector(Arrays.copyOfRange(bits, start, end));
- }
-
- private void checkCompatibility(BitVector bv)
- {
- if (length() != bv.length())
- throw new IllegalArgumentException(format("BitVector length does not match: %d and %d", length(), bv.length()));
- }
-
- static Bit[] binOp(Bit[] dest, Bit[] second, BinaryOperator<Bit> op)
- {
- if (dest == null)
- return second.clone();
- for (int i = 0; i < dest.length; i++)
- {
- dest[i] = op.apply(dest[i], second[i]);
- }
- return dest;
- }
-
- static Bit[] unOp(Bit[] dest, UnaryOperator<Bit> op)
- {
- if (dest == null)
- return null;
- for (int i = 0; i < dest.length; i++)
- {
- dest[i] = op.apply(dest[i]);
- }
- return dest;
- }
-
- /**
- * Class for comfortable and efficient manipulation of {@link BitVector}s, similar to {@link StringBuilder}
- *
- * @author Christian Femers
- */
- @SuppressWarnings("synthetic-access")
- public static final class BitVectorMutator implements LogicType<BitVectorMutator, BitVector>
- {
- private Bit[] bits;
-
- private BitVectorMutator(Bit[] bits)
- {
- this.bits = bits;
- }
-
- static BitVectorMutator of(BitVector bv)
- {
- return new BitVectorMutator(bv.getBits());
- }
-
- /**
- * Returns an empty mutator which has no bits set and will simply copy the values from the first binary operation performed.
- *
- */
- public static BitVectorMutator empty()
- {
- return new BitVectorMutator(null);
- }
-
- /**
- * Produces the resulting, immutable {@link BitVector}<br>
- *
- * @throws IllegalStateException if the mutator is (still) empty
- */
- public BitVector get()
- {
- if (bits == null)
- throw new IllegalStateException("cannot create a BitVector from an empty mutator");
- return new BitVector(bits);
- }
-
- @Override
- public BitVectorMutator join(BitVector t)
- {
- checkCompatibility(t);
- bits = binOp(bits, t.bits, Bit::join);
- return this;
- }
-
- @Override
- public BitVectorMutator and(BitVector t)
- {
- checkCompatibility(t);
- bits = binOp(bits, t.bits, Bit::and);
- return this;
- }
-
- @Override
- public BitVectorMutator or(BitVector t)
- {
- checkCompatibility(t);
- bits = binOp(bits, t.bits, Bit::or);
- return this;
- }
-
- @Override
- public BitVectorMutator xor(BitVector t)
- {
- checkCompatibility(t);
- bits = binOp(bits, t.bits, Bit::xor);
- return this;
- }
-
- @Override
- public BitVectorMutator not()
- {
- unOp(bits, Bit::not);
- return this;
- }
-
- private void checkCompatibility(BitVector bv)
- {
- if (bits != null && bits.length != bv.length())
- throw new IllegalArgumentException(format("BitVector length does not match: %d and %d", bits.length, bv.length()));
- }
- }
-
- /**
- * @see Arrays#hashCode(Object[])
- */
- @Override
- public int hashCode()
- {
- return Arrays.hashCode(bits);
- }
-
- /**
- * Does test for equality of values/content
- *
- * @see Object#equals(Object)
- */
- @Override
- public boolean equals(Object obj)
- {
- if (this == obj)
- return true;
- if (!(obj instanceof BitVector))
- return false;
- BitVector other = (BitVector) obj;
- return Arrays.equals(bits, other.bits);
- }
-
- /**
- * Does test for equality of values/content, shifting the other BitVector by <code>offset</code> to the right.<br>
- * Therefore <code>offset + other.length() <= this.length()</code> needs to be true.
- *
- * @throws ArrayIndexOutOfBoundsException if <code>offset + other.length() > this.length()</code>
- *
- * @see Object#equals(Object)
- */
- public boolean equalsWithOffset(BitVector other, int offset)
- {
- if (other == null)
- return false;
- return Arrays.equals(bits, offset, offset + other.length(), other.bits, 0, other.length());
- }
-
- /**
- * All {@link Bit}s symbols concatenated together
- *
- * @see #parse(String)
- */
- @Override
- public String toString()
- {
- StringBuilder sb = new StringBuilder(bits.length);
- for (Bit bit : bits)
- sb.append(bit);
- return sb.toString();
- }
-
- /**
- * Parses a String containing solely {@link Bit} symbols
- *
- * @see #toString()
- */
- public static BitVector parse(String s)
- {
- Bit[] values = new Bit[s.length()];
- for (int i = 0; i < s.length(); i++)
- {
- values[i] = Bit.parse(s, i);
- }
- return new BitVector(values);
- }
-
- @Override
- public Iterator<Bit> iterator()
- {
- return new Iterator<>()
- {
- private int pos = 0;
-
- @Override
- public Bit next()
- {
- if (!hasNext())
- throw new NoSuchElementException();
- return getBit(pos++);
- }
-
- @Override
- public boolean hasNext()
- {
- return pos != length();
- }
- };
- }
-}
+++ /dev/null
-package era.mi.logic.types;
-
-import era.mi.logic.types.ColorDefinition.BuiltInColor;
-import era.mi.logic.wires.Wire.ReadEnd;
-
-public class BitVectorFormatter
-{
- public static String formatValueAsString(ReadEnd end)
- {
- return formatAsString(end == null ? null : end.getValues());
- }
-
- public static String formatAsString(BitVector bitVector)
- {
- if (bitVector == null)
- return "null";
- else
- return bitVector.toString();
- }
-
- public static ColorDefinition formatAsColor(ReadEnd end)
- {
- return formatAsColor(end == null ? null : end.getValues());
- }
-
- public static ColorDefinition formatAsColor(BitVector bitVector)
- {
- // TODO maybe find a color assignment for multiple-bit bit vectors?
- if (bitVector == null || bitVector.length() != 1)
- return new ColorDefinition(BuiltInColor.COLOR_BLACK);
- else
- switch (bitVector.getBit(0))
- {
- case ONE:
- return new ColorDefinition(BuiltInColor.COLOR_GREEN);
- case U:
- return new ColorDefinition(BuiltInColor.COLOR_CYAN);
- case X:
- return new ColorDefinition(BuiltInColor.COLOR_RED);
- case Z:
- return new ColorDefinition(BuiltInColor.COLOR_YELLOW);
- case ZERO:
- return new ColorDefinition(BuiltInColor.COLOR_GRAY);
- default:
- throw new IllegalArgumentException("Unknown enum constant: " + bitVector.getBit(0));
- }
- }
-
- private BitVectorFormatter()
- {
- throw new UnsupportedOperationException("No BitVectorFormatter instances");
- }
-}
\ No newline at end of file
+++ /dev/null
-package era.mi.logic.types;
-
-/**
- * A way to define a color with the possibility to use colors built into the system (called "system colors" in SWT).
- * <p>
- * A {@link ColorDefinition} is defined either by a {@link BuiltInColor} constant, in which case <code>r==g==b==-1</code>, or by red / green
- * / blue components, in which case <code>builtInColor==null</code>
- */
-public class ColorDefinition
-{
- /**
- * The built-in color constant defining this color.
- */
- public final ColorDefinition.BuiltInColor builtInColor;
- /**
- * The red color component defining this color.
- */
- public final int r;
- /**
- * The green color component defining this color.
- */
- public final int g;
- /**
- * The blue color component defining this color.
- */
- public final int b;
-
- public ColorDefinition(ColorDefinition.BuiltInColor col)
- {
- if (col == null)
- throw new IllegalArgumentException("Illegal built-in color: " + col);
- this.builtInColor = col;
- this.r = -1;
- this.g = -1;
- this.b = -1;
- }
-
- public ColorDefinition(int r, int g, int b)
- {
- if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255)
- throw new IllegalArgumentException("Illegal color components: r=" + r + "; g=" + g + "; b=" + b);
- this.builtInColor = null;
- this.r = r;
- this.g = g;
- this.b = b;
- }
-
- public static enum BuiltInColor
- {
- COLOR_WHITE, COLOR_BLACK, COLOR_RED, COLOR_DARK_RED, COLOR_GREEN, COLOR_DARK_GREEN, COLOR_YELLOW, COLOR_DARK_YELLOW, COLOR_BLUE,
- COLOR_DARK_BLUE, COLOR_MAGENTA, COLOR_DARK_MAGENTA, COLOR_CYAN, COLOR_DARK_CYAN, COLOR_GRAY, COLOR_DARK_GRAY;
- }
-
-}
\ No newline at end of file
+++ /dev/null
-package era.mi.logic.types;
-
-/**
- * Interface for types that support the basic logic operations
- *
- * @author Christian Femers
- *
- * @param <T> the logic type itself, to make the operations closed in T
- * @param <S> the operand type, may be the same as T, see {@link StrictLogicType}
- */
-public interface LogicType<T extends LogicType<T, S>, S>
-{
- /**
- * Determines the result when two signals meet each other directly, also called resolution (IEEE 1164)
- *
- * For example:
- *
- * <pre>
- * 0 joined 0 == 0
- * 1 joined 0 == X
- * 0 joined 1 == X
- * 1 joined 1 == 1
- * </pre>
- *
- * @param t the second logic signal
- * @return the resulting value
- * @author Christian Femers
- */
- T join(S t);
-
- /**
- * Classical logic AND
- *
- * For example:
- *
- * <pre>
- * 0 AND 0 == 0
- * 1 AND 0 == 0
- * 0 AND 1 == 0
- * 1 AND 1 == 1
- * </pre>
- *
- * @param t the second logic signal
- * @return the resulting value
- * @author Christian Femers
- */
- T and(S t);
-
- /**
- * Classical logic OR
- *
- * For example:
- *
- * <pre>
- * 0 OR 0 == 0
- * 1 OR 0 == 1
- * 0 OR 1 == 1
- * 1 OR 1 == 1
- * </pre>
- *
- * @param t the second logic signal
- * @return the resulting value
- * @author Christian Femers
- */
- T or(S t);
-
- /**
- * Classical logic XOR (exclusive OR)
- *
- * For example:
- *
- * <pre>
- * 0 XOR 0 == 0
- * 1 XOR 0 == 1
- * 0 XOR 1 == 1
- * 1 XOR 1 == 0
- * </pre>
- *
- * @param t the second logic signal
- * @return the resulting value
- * @author Christian Femers
- */
- T xor(S t);
-
- /**
- * Classical logic NOT (logical negation)
- *
- * For example:
- *
- * <pre>
- * NOT 0 == 1
- * NOT 1 == 0
- * </pre>
- *
- * @return the resulting value
- * @author Christian Femers
- */
- T not();
-
- /**
- * {@link #and(Object) AND} and then {@link #not() NOT}
- *
- * @author Christian Femers
- */
- default T nand(S t)
- {
- return and(t).not();
- }
-
- /**
- * {@link #or(Object) OR} and then {@link #not() NOT}
- *
- * @author Christian Femers
- */
- default T nor(S t)
- {
- return or(t).not();
- }
-
- /**
- * {@link #xor(Object) XOR} and then {@link #not() NOT}<br>
- * Used to determine equality (alias parity, consistency)
- *
- * @author Christian Femers
- */
- default T xnor(S t)
- {
- return xor(t).not();
- }
-}
+++ /dev/null
-package era.mi.logic.types;
-
-import java.util.function.BiFunction;
-
-import era.mi.logic.types.BitVector.BitVectorMutator;
-
-@FunctionalInterface
-public interface MutationOperation extends BiFunction<BitVectorMutator, BitVector, BitVectorMutator>
-{
- // no changes necessary, only for convenience and readability
-}
+++ /dev/null
-package era.mi.logic.types;
-
-/**
- * Interface for types that support the basic logic operations only among themselves, making it mathematically closed
- *
- * @author Christian Femers
- * @see LogicType
- *
- * @param <T> the logic type itself to make the operations closed
- */
-public interface StrictLogicType<T extends StrictLogicType<T>> extends LogicType<T, T>
-{
- // this is just a matter of type parameters
-}
+++ /dev/null
-package era.mi.logic.wires;
-
-import static era.mi.logic.types.Bit.U;
-import static era.mi.logic.types.Bit.Z;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import era.mi.logic.timeline.Timeline;
-import era.mi.logic.types.Bit;
-import era.mi.logic.types.BitVector;
-import era.mi.logic.types.BitVector.BitVectorMutator;
-
-/**
- * Represents an array of wires that can store n bits of information.
- *
- * @author Fabian Stemmler
- *
- */
-public class Wire
-{
- private BitVector values;
- public final int travelTime;
- private List<ReadEnd> attached = new ArrayList<ReadEnd>();
- public final int length;
- private List<ReadWriteEnd> inputs = new ArrayList<ReadWriteEnd>();
- private Timeline timeline;
-
- public Wire(Timeline timeline, int length, int travelTime)
- {
- if (length < 1)
- throw new IllegalArgumentException(
- String.format("Tried to create an array of wires with length %d, but a length of less than 1 makes no sense.", length));
- this.timeline = timeline;
- this.length = length;
- this.travelTime = travelTime;
- initValues();
- }
-
- private void initValues()
- {
- values = U.toVector(length);
- }
-
- private void recalculateSingleInput()
- {
- setNewValues(inputs.get(0).getInputValues());
- }
-
- private void recalculateMultipleInputs()
- {
- BitVectorMutator mutator = BitVectorMutator.empty();
- for (ReadWriteEnd wireArrayEnd : inputs)
- mutator.join(wireArrayEnd.getInputValues());
- setNewValues(mutator.get());
- }
-
- private void setNewValues(BitVector newValues)
- {
- if (values.equals(newValues))
- return;
- BitVector oldValues = values;
- values = newValues;
- notifyObservers(oldValues);
- }
-
- private void recalculate()
- {
- switch (inputs.size())
- {
- case 0:
- return;
- case 1:
- recalculateSingleInput();
- break;
- default:
- recalculateMultipleInputs();
- }
- }
-
- /**
- * The {@link Wire} is interpreted as an unsigned integer with n bits.
- *
- * @return <code>true</code> if all bits are either <code>Bit.ONE</code> or <code>Bit.ZERO</code> (they do not all have to have the same
- * value), not <code>Bit.X</code> or <code>Bit.Z</code>. <code>false</code> is returned otherwise.
- *
- * @author Fabian Stemmler
- */
- public boolean hasNumericValue()
- {
- for (Bit b : values)
- {
- if (b != Bit.ZERO && b != Bit.ONE)
- return false;
- }
- return true;
- }
-
- /**
- * The {@link Wire} is interpreted as an unsigned integer with n bits.
- *
- * @return The unsigned value of the {@link Wire}'s bits, where value 0 corresponds with 2^0, value 1 is 2^1 and so on.
- *
- * @author Fabian Stemmler
- */
- public long getUnsignedValue()
- {
- long val = 0;
- long mask = 1;
- for (Bit bit : values)
- {
- switch (bit)
- {
- default:
- case Z:
- case X:
- return 0; // TODO: Proper handling for getUnsignedValue(), if not all bits are 1 or 0;
- case ONE:
- val |= mask;
- break;
- case ZERO:
- }
- mask = mask << 1;
- }
- return val;
- }
-
- /**
- * The {@link Wire} is interpreted as a signed integer with n bits.
- *
- * @return The signed value of the {@link Wire}'s bits, where value 0 corresponds with 2^0, value 1 is 2^1 and so on.
- *
- * @author Fabian Stemmler
- */
- public long getSignedValue()
- {
- long val = getUnsignedValue();
- long mask = 1 << (length - 1);
- if ((mask & val) != 0)
- {
- int shifts = 64 - length;
- return (val << shifts) >> shifts;
- }
- return val;
- }
-
- public Bit getValue()
- {
- return getValue(0);
- }
-
- public Bit getValue(int index)
- {
- return values.getBit(index);
- }
-
- public BitVector getValues(int start, int end)
- {
- return values.subVector(start, end);
- }
-
- public BitVector getValues()
- {
- return values;
- }
-
- /**
- * Adds an {@link WireObserver}, who will be notified when the value of the {@link Wire} is updated.
- *
- * @param ob The {@link WireObserver} to be notified of changes.
- * @return true if the given {@link WireObserver} was not already registered, false otherwise
- *
- * @author Fabian Stemmler
- */
- private void attachEnd(ReadEnd end)
- {
- attached.add(end);
- }
-
- private void detachEnd(ReadEnd end)
- {
- attached.remove(end);
- }
-
- private void notifyObservers(BitVector oldValues)
- {
- for (ReadEnd o : attached)
- o.update(oldValues);
- }
-
- /**
- * Create and register a {@link ReadWriteEnd} object, which is tied to this {@link Wire}. This {@link ReadWriteEnd} can be written to.
- */
- public ReadWriteEnd createReadWriteEnd()
- {
- return new ReadWriteEnd();
- }
-
- /**
- * Create a {@link ReadEnd} object, which is tied to this {@link Wire}. This {@link ReadEnd} cannot be written to.
- */
- public ReadEnd createReadOnlyEnd()
- {
- return new ReadEnd();
- }
-
- private void registerInput(ReadWriteEnd toRegister)
- {
- inputs.add(toRegister);
- }
-
- /**
- * A {@link ReadEnd} feeds a constant signal into the {@link Wire} it is tied to. The combination of all inputs determines the
- * {@link Wire}s final value. X dominates all other inputs Z does not affect the final value, unless there are no other inputs than Z 0
- * and 1 turn into X when they are mixed
- *
- * @author Fabian Stemmler
- */
- public class ReadEnd
- {
- private List<WireObserver> observers = new ArrayList<WireObserver>();
-
- private ReadEnd()
- {
- super();
- Wire.this.attachEnd(this);
- }
-
- public void update(BitVector oldValues)
- {
- for (WireObserver ob : observers)
- ob.update(this, oldValues);
- }
-
- /**
- * Included for convenient use on {@link Wire}s of length 1.
- *
- * @return The value of bit 0.
- *
- * @author Fabian Stemmler
- */
- public Bit getValue()
- {
- return Wire.this.getValue();
- }
-
- /**
- * @param index Index of the requested bit.
- * @return The value of the indexed bit.
- *
- * @author Fabian Stemmler
- */
- public Bit getValue(int index)
- {
- return Wire.this.getValue(index);
- }
-
- /**
- * @param index Index of the requested bit.
- * @return The value of the indexed bit.
- *
- * @author Fabian Stemmler
- */
- public BitVector getValues()
- {
- return Wire.this.getValues();
- }
-
- /**
- * @param start Start of the wanted segment. (inclusive)
- * @param end End of the wanted segment. (exclusive)
- * @return The values of the segment of {@link Bit}s indexed.
- *
- * @author Fabian Stemmler
- */
- public BitVector getValues(int start, int end)
- {
- return Wire.this.getValues(start, end);
- }
-
- /**
- * The {@link Wire} is interpreted as an unsigned integer with n bits.
- *
- * @return <code>true</code> if all bits are either <code>Bit.ONE</code> or <code>Bit.ZERO</code> (they do not all have to have the
- * same value), not <code>Bit.X</code> or <code>Bit.Z</code>. <code>false</code> is returned otherwise.
- *
- * @author Fabian Stemmler
- */
- public boolean hasNumericValue()
- {
- return Wire.this.hasNumericValue();
- }
-
- /**
- * The {@link Wire} is interpreted as an unsigned integer with n bits.
- *
- * @return The unsigned value of the {@link Wire}'s bits, where value 0 corresponds with 2^0, value 1 is 2^1 and so on.
- *
- * @author Fabian Stemmler
- */
- public long getUnsignedValue()
- {
- return Wire.this.getUnsignedValue();
- }
-
- /**
- * The {@link Wire} is interpreted as a signed integer with n bits.
- *
- * @return The signed value of the {@link Wire}'s bits, where value 0 corresponds with 2^0, value 1 is 2^1 and so on.
- *
- * @author Fabian Stemmler
- */
- public long getSignedValue()
- {
- return Wire.this.getSignedValue();
- }
-
- @Override
- public String toString()
- {
- return Wire.this.toString();
- }
-
- public void close()
- {
- inputs.remove(this);
- detachEnd(this);
- recalculate();
- }
-
- public int length()
- {
- return length;
- }
-
- public boolean addObserver(WireObserver ob)
- {
- return observers.add(ob);
- }
-
- public Wire getWire()
- {
- return Wire.this;
- }
- }
-
- public class ReadWriteEnd extends ReadEnd
- {
- private boolean open;
- private BitVector inputValues;
-
- private ReadWriteEnd()
- {
- super();
- open = true;
- initValues();
- registerInput(this);
- }
-
- private void initValues()
- {
- inputValues = U.toVector(length);
- }
-
- /**
- * Sets the wires values. This takes up time, as specified by the {@link Wire}s travel time.
- *
- * @param newValues The new values the wires should take on.
- *
- * @author Fabian Stemmler
- */
- public void feedSignals(Bit... newValues)
- {
- feedSignals(BitVector.of(newValues));
- }
-
- public void feedSignals(BitVector newValues)
- {
- if (newValues.length() != length)
- throw new IllegalArgumentException(
- String.format("Attempted to input %d bits instead of %d bits.", newValues.length(), length));
- if (!open)
- throw new RuntimeException("Attempted to write to closed WireArrayEnd.");
- timeline.addEvent(e -> setValues(newValues), travelTime);
- }
-
- /**
- * Sets values of a subarray of wires. This takes up time, as specified by the {@link Wire}s travel time.
- *
- * @param bitVector The new values the wires should take on.
- * @param startingBit The first index of the subarray of wires.
- *
- * @author Fabian Stemmler
- */
- public void feedSignals(int startingBit, BitVector bitVector)
- {
- if (!open)
- throw new RuntimeException("Attempted to write to closed WireArrayEnd.");
- timeline.addEvent(e -> setValues(startingBit, bitVector), travelTime);
- }
-
- private void setValues(int startingBit, BitVector newValues)
- {
- // index check covered in equals
- if (!inputValues.equalsWithOffset(newValues, startingBit))
- {
- Bit[] vals = inputValues.getBits();
- System.arraycopy(newValues.getBits(), 0, vals, startingBit, newValues.length());
- inputValues = BitVector.of(vals);
- Wire.this.recalculate();
- }
- }
-
- private void setValues(BitVector newValues)
- {
- if (inputValues.equals(newValues))
- return;
- inputValues = newValues;
- Wire.this.recalculate();
- }
-
- /**
- * @return The value (of bit 0) the {@link ReadEnd} is currently feeding into the associated {@link Wire}.
- */
- public Bit getInputValue()
- {
- return getInputValue(0);
- }
-
- /**
- * @return The value which the {@link ReadEnd} is currently feeding into the associated {@link Wire} at the indexed {@link Bit}.
- */
- public Bit getInputValue(int index)
- {
- return inputValues.getBit(index);
- }
-
- /**
- * @return A copy (safe to modify) of the values the {@link ReadEnd} is currently feeding into the associated {@link Wire}.
- */
- public BitVector getInputValues()
- {
- return getInputValues(0, length);
- }
-
- public BitVector getInputValues(int start, int end)
- {
- return inputValues.subVector(start, end);
- }
-
- /**
- * {@link ReadEnd} now feeds Z into the associated {@link Wire}.
- */
- public void clearSignals()
- {
- feedSignals(Z.toVector(length));
- }
-
- public BitVector wireValuesExcludingMe()
- {
- BitVectorMutator mutator = BitVectorMutator.empty();
- for (ReadWriteEnd wireEnd : inputs)
- {
- if (wireEnd == this)
- continue;
- mutator.join(wireEnd.inputValues);
- }
- return mutator.get();
- }
-
- @Override
- public String toString()
- {
- return inputValues.toString();
- }
- }
-
- @Override
- public String toString()
- {
- return String.format("wire 0x%08x value: %s inputs: %s", hashCode(), values, inputs);
- // Arrays.toString(values), inputs.stream().map(i -> Arrays.toString(i.inputValues)).reduce((s1, s2) -> s1 + s2)
- }
-
- public static ReadEnd[] extractEnds(Wire[] w)
- {
- ReadEnd[] inputs = new ReadEnd[w.length];
- for (int i = 0; i < w.length; i++)
- inputs[i] = w[i].createReadWriteEnd();
- return inputs;
- }
-}
\ No newline at end of file
+++ /dev/null
-package era.mi.logic.wires;
-
-import era.mi.logic.types.BitVector;
-import era.mi.logic.wires.Wire.ReadEnd;
-
-public interface WireObserver
-{
- public void update(ReadEnd initiator, BitVector oldValues);
-}
--- /dev/null
+package mograsim.logic.core;
+
+import java.util.Arrays;
+
+import mograsim.logic.core.types.Bit;
+
+public final class Util
+{
+
+ @SuppressWarnings("unchecked")
+ public static <T> T[] concat(T[]... arrays)
+ {
+ if (arrays.length == 0)
+ throw new IllegalArgumentException("Cannot concatenate 0 arrays.");
+
+ int length = 0;
+ for (T[] array : arrays)
+ length += array.length;
+
+ T[] newArray = Arrays.copyOf(arrays[0], length);
+ int appendIndex = arrays[0].length;
+ for (int i = 1; i < arrays.length; i++)
+ {
+ System.arraycopy(arrays[i], 0, newArray, appendIndex, arrays[i].length);
+ appendIndex += arrays[i].length;
+ }
+
+ return newArray;
+ }
+
+// @SuppressWarnings("unchecked")
+// public static <T> T[][] split(T[] array, int... lengths)
+// {
+// //TODO: implement array split again; This version contains an illegal cast
+// int totalLength = 0;
+// for(int length : lengths)
+// totalLength += length;
+//
+// if(totalLength != array.length)
+// throw new IllegalArgumentException(); //TODO: add proper error message
+//
+// Object[][] newArray = new Object[lengths.length][];
+// int splitIndex = 0;
+// for(int i = 0; i < lengths.length; i++)
+// {
+// System.arraycopy(array, splitIndex, newArray, 0, lengths[i]);
+// splitIndex += lengths[i];
+// }
+//
+// return (T[][]) newArray;
+// }
+
+ public static Bit[] and(Bit[] a, Bit[] b)
+ {
+ return binBitOp(a, b, Bit::and);
+ }
+
+ public static Bit[] or(Bit[] a, Bit[] b)
+ {
+ return binBitOp(a, b, Bit::or);
+ }
+
+ public static Bit[] xor(Bit[] a, Bit[] b)
+ {
+ return binBitOp(a, b, Bit::xor);
+ }
+
+ private static Bit[] binBitOp(Bit[] a, Bit[] b, BitOp op)
+ {
+ if (a.length != b.length)
+ throw new IllegalArgumentException("Bit Arrays were not of equal length.");
+ Bit[] out = new Bit[a.length];
+ for (int i = 0; i < a.length; i++)
+ {
+ out[i] = op.execute(a[i], b[i]);
+ }
+ return out;
+ }
+
+ public static Bit[] not(Bit[] a)
+ {
+ Bit[] out = new Bit[a.length];
+ for (int i = 0; i < a.length; i++)
+ {
+ out[i] = a[i].not();
+ }
+ return out;
+ }
+
+ /**
+ * uses the {@link Bit#combineWith(Bit)} method, does not create a new array, the result is stored in the first array.
+ *
+ * @author Christian Femers
+ */
+ public static Bit[] combineInto(Bit[] dest, Bit[] addition)
+ {
+ if (dest.length != addition.length)
+ throw new IllegalArgumentException("Bit Arrays were not of equal length.");
+ for (int i = 0; i < addition.length; i++)
+ {
+ dest[i] = dest[i].join(addition[i]);
+ }
+ return dest;
+ }
+
+ interface BitOp
+ {
+ Bit execute(Bit a, Bit b);
+ }
+}
--- /dev/null
+package mograsim.logic.core.components;
+
+import mograsim.logic.core.timeline.Timeline;
+import mograsim.logic.core.types.BitVector;
+import mograsim.logic.core.wires.WireObserver;
+import mograsim.logic.core.wires.Wire.ReadEnd;
+
+/**
+ * A basic component that recomputes all outputs (with a delay), when it is updated.
+ *
+ * @author Fabian Stemmler
+ */
+public abstract class BasicComponent extends Component implements WireObserver
+{
+ private int processTime;
+
+ /**
+ *
+ * @param processTime Amount of time this component takes to update its outputs. Must be more than 0, otherwise 1 is assumed.
+ *
+ * @author Fabian Stemmler
+ */
+ public BasicComponent(Timeline timeline, int processTime)
+ {
+ super(timeline);
+ this.processTime = processTime > 0 ? processTime : 1;
+ }
+
+ @Override
+ public void update(ReadEnd initiator, BitVector oldValues)
+ {
+ timeline.addEvent(e -> compute(), processTime);
+ }
+
+ protected abstract void compute();
+}
--- /dev/null
+package mograsim.logic.core.components;
+
+import java.util.List;
+
+import mograsim.logic.core.timeline.Timeline;
+import mograsim.logic.core.types.Bit;
+import mograsim.logic.core.types.BitVector;
+import mograsim.logic.core.wires.Wire.ReadEnd;
+import mograsim.logic.core.wires.Wire.ReadWriteEnd;
+
+public class BitDisplay extends BasicComponent
+{
+ private final ReadEnd in;
+ private BitVector displayedValue;
+
+ public BitDisplay(Timeline timeline, ReadEnd in)
+ {
+ super(timeline, 1);
+ this.in = in;
+ in.addObserver(this);
+ compute();
+ }
+
+ @Override
+ protected void compute()
+ {
+ displayedValue = in.getValues();
+ }
+
+ public BitVector getDisplayedValue()
+ {
+ return displayedValue;
+ }
+
+ public boolean isDisplaying(Bit... values)
+ {
+ return displayedValue.equals(BitVector.of(values));
+ }
+
+ @Override
+ public List<ReadEnd> getAllInputs()
+ {
+ return List.of(in);
+ }
+
+ @Override
+ public List<ReadWriteEnd> getAllOutputs()
+ {
+ return List.of();
+ }
+}
--- /dev/null
+package mograsim.logic.core.components;
+
+import java.util.List;
+
+import mograsim.logic.core.timeline.Timeline;
+import mograsim.logic.core.timeline.TimelineEvent;
+import mograsim.logic.core.timeline.TimelineEventHandler;
+import mograsim.logic.core.types.Bit;
+import mograsim.logic.core.wires.Wire;
+import mograsim.logic.core.wires.Wire.ReadEnd;
+import mograsim.logic.core.wires.Wire.ReadWriteEnd;
+
+public class Clock extends Component implements TimelineEventHandler
+{
+ private boolean toggle = false;
+ private ReadWriteEnd out;
+ private int delta;
+
+ /**
+ *
+ * @param out {@link Wire} the clock's impulses are fed into
+ * @param delta ticks between rising and falling edge
+ */
+ public Clock(Timeline timeline, ReadWriteEnd out, int delta)
+ {
+ super(timeline);
+ this.delta = delta;
+ this.out = out;
+ addToTimeline();
+ }
+
+ @Override
+ public void handle(TimelineEvent e)
+ {
+ addToTimeline();
+ out.feedSignals(toggle ? Bit.ONE : Bit.ZERO);
+ toggle = !toggle;
+ }
+
+ public ReadWriteEnd getOut()
+ {
+ return out;
+ }
+
+ private void addToTimeline()
+ {
+ timeline.addEvent(this, delta);
+ }
+
+ @Override
+ public List<ReadEnd> getAllInputs()
+ {
+ return List.of();
+ }
+
+ @Override
+ public List<ReadWriteEnd> getAllOutputs()
+ {
+ return List.of(out);
+ }
+}
--- /dev/null
+package mograsim.logic.core.components;
+
+import java.util.List;
+
+import mograsim.logic.core.timeline.Timeline;
+import mograsim.logic.core.wires.Wire.ReadEnd;
+import mograsim.logic.core.wires.Wire.ReadWriteEnd;
+
+public abstract class Component
+{
+ protected Timeline timeline;
+
+ public Component(Timeline timeline)
+ {
+ this.timeline = timeline;
+ }
+
+ /**
+ * Returns immutable list of all inputs to the {@link Component} (including e.g. the select bits to a MUX). Intended for visualization
+ * in the UI.
+ */
+ public abstract List<ReadEnd> getAllInputs();
+
+ /**
+ * Returns immutable list of all outputs to the {@link Component}. Intended for visualization in the UI.
+ */
+ public abstract List<ReadWriteEnd> getAllOutputs();
+}
--- /dev/null
+package mograsim.logic.core.components;
+
+import java.util.List;
+
+import mograsim.logic.core.timeline.Timeline;
+import mograsim.logic.core.types.BitVector;
+import mograsim.logic.core.wires.WireObserver;
+import mograsim.logic.core.wires.Wire.ReadEnd;
+import mograsim.logic.core.wires.Wire.ReadWriteEnd;
+
+public class Connector extends Component implements WireObserver
+{
+ private boolean connected;
+ private final ReadWriteEnd a;
+ private final ReadWriteEnd b;
+
+ public Connector(Timeline timeline, ReadWriteEnd a, ReadWriteEnd b)
+ {
+ super(timeline);
+ if (a.length() != b.length())
+ throw new IllegalArgumentException(String.format("WireArray width does not match: %d, %d", a.length(), b.length()));
+ this.a = a;
+ this.b = b;
+ a.addObserver(this);
+ b.addObserver(this);
+ }
+
+ public void connect()
+ {
+ connected = true;
+ update(a);
+ update(b);
+ }
+
+ public void disconnect()
+ {
+ connected = false;
+ a.clearSignals();
+ b.clearSignals();
+ }
+
+ public void setConnection(boolean connected)
+ {
+ if (connected)
+ connect();
+ else
+ disconnect();
+ }
+
+ @Override
+ public void update(ReadEnd initiator, BitVector oldValues)
+ {
+ if (connected)
+ timeline.addEvent(e -> update(initiator), 1);
+ }
+
+ private void update(ReadEnd initiator)
+ {
+ if (initiator == a)
+ b.feedSignals(a.wireValuesExcludingMe());
+ else
+ a.feedSignals(b.wireValuesExcludingMe());
+ }
+
+ @Override
+ public List<ReadEnd> getAllInputs()
+ {
+ return List.of(a, b);
+ }
+
+ @Override
+ public List<ReadWriteEnd> getAllOutputs()
+ {
+ return List.of(a, b);
+ }
+}
--- /dev/null
+package mograsim.logic.core.components;
+
+import java.util.List;
+
+import mograsim.logic.core.timeline.Timeline;
+import mograsim.logic.core.wires.Wire;
+import mograsim.logic.core.wires.Wire.ReadEnd;
+import mograsim.logic.core.wires.Wire.ReadWriteEnd;
+
+/**
+ * Models a multiplexer. Takes an arbitrary amount of input {@link Wire}s, one of which, as determined by select, is put through to the
+ * output.
+ *
+ * @author Fabian Stemmler
+ *
+ */
+public class Demux extends BasicComponent
+{
+ private final ReadEnd select, in;
+ private final ReadWriteEnd[] outputs;
+ private final int outputSize;
+ private int selected = -1;
+
+ /**
+ * Output {@link Wire}s and in must be of uniform length
+ *
+ * @param in Must be of uniform length with all outputs.
+ * @param select Indexes the output array to which the input is mapped. Must have enough bits to index all outputs.
+ * @param outputs One of these outputs receives the input signal, depending on the select bits
+ */
+ public Demux(Timeline timeline, int processTime, ReadEnd in, ReadEnd select, ReadWriteEnd... outputs)
+ {
+ super(timeline, processTime);
+ outputSize = in.length();
+
+ this.in = in;
+ this.outputs = outputs;
+ for (int i = 0; i < this.outputs.length; i++)
+ {
+ if (outputs[i].length() != outputSize)
+ throw new IllegalArgumentException("All DEMUX wire arrays must be of uniform length!");
+ this.outputs[i] = outputs[i];
+ }
+
+ this.select = select;
+ select.addObserver(this);
+
+ int maxInputs = 1 << select.length();
+ if (this.outputs.length > maxInputs)
+ throw new IllegalArgumentException("There are more outputs (" + this.outputs.length + ") to the DEMUX than supported by "
+ + select.length() + " select bits (" + maxInputs + ").");
+ in.addObserver(this);
+ }
+
+ @Override
+ public void compute()
+ {
+ int selectValue = select.hasNumericValue() ? (int) select.getUnsignedValue() : -1;
+ if (selectValue >= outputs.length)
+ selectValue = -1;
+
+ if (selected != selectValue && selected != -1)
+ outputs[selected].clearSignals();
+
+ selected = selectValue;
+
+ if (selectValue != -1)
+ outputs[selectValue].feedSignals(in.getValues());
+ }
+
+ @Override
+ public List<ReadEnd> getAllInputs()
+ {
+ return List.of(in, select);
+ }
+
+ @Override
+ public List<ReadWriteEnd> getAllOutputs()
+ {
+ return List.of(outputs);
+ }
+}
--- /dev/null
+package mograsim.logic.core.components;
+
+import java.util.List;
+
+import mograsim.logic.core.timeline.Timeline;
+import mograsim.logic.core.types.Bit;
+import mograsim.logic.core.wires.Wire.ReadEnd;
+import mograsim.logic.core.wires.Wire.ReadWriteEnd;
+
+/**
+ * This class models a simple on/off (ONE/ZERO) switch for user interaction.
+ *
+ * @author Christian Femers
+ *
+ */
+public class ManualSwitch extends Component
+{
+ private ReadWriteEnd output;
+ private boolean isOn;
+
+ public ManualSwitch(Timeline timeline, ReadWriteEnd output)
+ {
+ super(timeline);
+ if (output.length() != 1)
+ throw new IllegalArgumentException("Switch output can be only a single wire");
+ this.output = output;
+ }
+
+ public void switchOn()
+ {
+ setState(true);
+ }
+
+ public void switchOff()
+ {
+ setState(false);
+ }
+
+ public void toggle()
+ {
+ setState(!isOn);
+ }
+
+ public void setState(boolean isOn)
+ {
+ if (this.isOn == isOn)
+ return;
+ this.isOn = isOn;
+ output.feedSignals(getValue());
+ }
+
+ public boolean isOn()
+ {
+ return isOn;
+ }
+
+ public Bit getValue()
+ {
+ return isOn ? Bit.ONE : Bit.ZERO;
+ }
+
+ @Override
+ public List<ReadEnd> getAllInputs()
+ {
+ return List.of();
+ }
+
+ @Override
+ public List<ReadWriteEnd> getAllOutputs()
+ {
+ return List.of(output);
+ }
+
+}
--- /dev/null
+package mograsim.logic.core.components;
+
+import java.util.List;
+
+import mograsim.logic.core.timeline.Timeline;
+import mograsim.logic.core.types.BitVector;
+import mograsim.logic.core.wires.Wire;
+import mograsim.logic.core.wires.WireObserver;
+import mograsim.logic.core.wires.Wire.ReadEnd;
+import mograsim.logic.core.wires.Wire.ReadWriteEnd;
+
+public class Merger extends Component implements WireObserver
+{
+ private ReadWriteEnd out;
+ private ReadEnd[] inputs;
+ private int[] beginningIndex;
+
+ /**
+ *
+ * @param union The output of merging n {@link Wire}s into one. Must have length = a1.length() + a2.length() + ... + an.length().
+ * @param inputs The inputs to be merged into the union
+ */
+ public Merger(Timeline timeline, ReadWriteEnd union, ReadEnd... inputs)
+ {
+ super(timeline);
+ this.inputs = inputs;
+ this.out = union;
+ this.beginningIndex = new int[inputs.length];
+
+ int length = 0;
+ for (int i = 0; i < inputs.length; i++)
+ {
+ beginningIndex[i] = length;
+ length += inputs[i].length();
+ inputs[i].addObserver(this);
+ }
+
+ if (length != union.length())
+ throw new IllegalArgumentException(
+ "The output of merging n WireArrays into one must have length = a1.length() + a2.length() + ... + an.length().");
+ }
+
+ public ReadEnd getInput(int index)
+ {
+ return inputs[index];
+ }
+
+ public ReadEnd getUnion()
+ {
+ return out;
+ }
+
+ @Override
+ public void update(ReadEnd initiator, BitVector oldValues)
+ {
+ int index = find(initiator);
+ int beginning = beginningIndex[index];
+ out.feedSignals(beginning, inputs[index].getValues());
+ }
+
+ private int find(ReadEnd r)
+ {
+ for (int i = 0; i < inputs.length; i++)
+ if (inputs[i] == r)
+ return i;
+ return -1;
+ }
+
+ public ReadEnd[] getInputs()
+ {
+ return inputs.clone();
+ }
+
+ @Override
+ public List<ReadEnd> getAllInputs()
+ {
+ return List.of(inputs);
+ }
+
+ @Override
+ public List<ReadWriteEnd> getAllOutputs()
+ {
+ return List.of(out);
+ }
+}
--- /dev/null
+package mograsim.logic.core.components;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import mograsim.logic.core.timeline.Timeline;
+import mograsim.logic.core.wires.Wire;
+import mograsim.logic.core.wires.Wire.ReadEnd;
+import mograsim.logic.core.wires.Wire.ReadWriteEnd;
+
+/**
+ * Models a multiplexer. Takes an arbitrary amount of input {@link Wire}s, one of which, as determined by select, is put through to the
+ * output.
+ *
+ * @author Fabian Stemmler
+ *
+ */
+public class Mux extends BasicComponent
+{
+ private ReadEnd select;
+ private ReadWriteEnd out;
+ private ReadEnd[] inputs;
+ private final int outputSize;
+
+ /**
+ * Input {@link Wire}s and out must be of uniform length
+ *
+ * @param out Must be of uniform length with all inputs.
+ * @param select Indexes the input array which is to be mapped to the output. Must have enough bits to index all inputs.
+ * @param inputs One of these inputs is mapped to the output, depending on the select bits
+ */
+ public Mux(Timeline timeline, int processTime, ReadWriteEnd out, ReadEnd select, ReadEnd... inputs)
+ {
+ super(timeline, processTime);
+ outputSize = out.length();
+
+ this.inputs = inputs.clone();
+ for (int i = 0; i < this.inputs.length; i++)
+ {
+ if (inputs[i].length() != outputSize)
+ throw new IllegalArgumentException("All MUX wire arrays must be of uniform length!");
+ inputs[i].addObserver(this);
+ }
+
+ this.select = select;
+ select.addObserver(this);
+
+ int maxInputs = 1 << select.length();
+ if (this.inputs.length > maxInputs)
+ throw new IllegalArgumentException("There are more inputs (" + this.inputs.length + ") to the MUX than supported by "
+ + select.length() + " select bits (" + maxInputs + ").");
+
+ this.out = out;
+ }
+
+ public ReadEnd getOut()
+ {
+ return out;
+ }
+
+ public ReadEnd getSelect()
+ {
+ return select;
+ }
+
+ @Override
+ public void compute()
+ {
+ int selectValue;
+ if (!select.hasNumericValue() || (selectValue = (int) select.getUnsignedValue()) >= inputs.length)
+ {
+ out.clearSignals();
+ return;
+ }
+
+ ReadEnd active = inputs[selectValue];
+ out.feedSignals(active.getValues());
+ }
+
+ @Override
+ public List<ReadEnd> getAllInputs()
+ {
+ ArrayList<ReadEnd> wires = new ArrayList<ReadEnd>(Arrays.asList(inputs));
+ wires.add(select);
+ return Collections.unmodifiableList(wires);
+ }
+
+ @Override
+ public List<ReadWriteEnd> getAllOutputs()
+ {
+ return List.of(out);
+ }
+}
--- /dev/null
+package mograsim.logic.core.components;
+
+import java.util.List;
+
+import mograsim.logic.core.timeline.Timeline;
+import mograsim.logic.core.types.BitVector;
+import mograsim.logic.core.wires.WireObserver;
+import mograsim.logic.core.wires.Wire.ReadEnd;
+import mograsim.logic.core.wires.Wire.ReadWriteEnd;
+
+public class Splitter extends Component implements WireObserver
+{
+ private ReadEnd input;
+ private ReadWriteEnd[] outputs;
+
+ public Splitter(Timeline timeline, ReadEnd input, ReadWriteEnd... outputs)
+ {
+ super(timeline);
+ this.input = input;
+ this.outputs = outputs;
+ input.addObserver(this);
+ int length = 0;
+ for (ReadEnd out : outputs)
+ length += out.length();
+
+ if (input.length() != length)
+ throw new IllegalArgumentException(
+ "The input of splitting one into n WireArrays must have length = a1.length() + a2.length() + ... + an.length().");
+ }
+
+ protected void compute()
+ {
+ BitVector inputBits = input.getValues();
+ int startIndex = 0;
+ for (int i = 0; i < outputs.length; i++)
+ {
+ outputs[i].feedSignals(inputBits.subVector(startIndex, startIndex + outputs[i].length()));
+ startIndex += outputs[i].length();
+ }
+ }
+
+ @Override
+ public void update(ReadEnd initiator, BitVector oldValues)
+ {
+ compute();
+ }
+
+ @Override
+ public List<ReadEnd> getAllInputs()
+ {
+ return List.of(input);
+ }
+
+ @Override
+ public List<ReadWriteEnd> getAllOutputs()
+ {
+ return List.of(outputs);
+ }
+}
--- /dev/null
+package mograsim.logic.core.components;
+
+import java.util.List;
+
+import mograsim.logic.core.timeline.Timeline;
+import mograsim.logic.core.types.Bit;
+import mograsim.logic.core.wires.Wire.ReadEnd;
+import mograsim.logic.core.wires.Wire.ReadWriteEnd;
+
+public class TriStateBuffer extends BasicComponent
+{
+ ReadEnd in, enable;
+ ReadWriteEnd out;
+
+ public TriStateBuffer(Timeline timeline, int processTime, ReadEnd in, ReadWriteEnd out, ReadEnd enable)
+ {
+ super(timeline, processTime);
+ if (in.length() != out.length())
+ throw new IllegalArgumentException(
+ "Tri-state output must have the same amount of bits as the input. Input: " + in.length() + " Output: " + out.length());
+ if (enable.length() != 1)
+ throw new IllegalArgumentException("Tri-state enable must have exactly one bit, not " + enable.length() + ".");
+ this.in = in;
+ in.addObserver(this);
+ this.enable = enable;
+ enable.addObserver(this);
+ this.out = out;
+ }
+
+ @Override
+ protected void compute()
+ {
+ if (enable.getValue() == Bit.ONE)
+ out.feedSignals(in.getValues());
+ else
+ out.clearSignals();
+ }
+
+ @Override
+ public List<ReadEnd> getAllInputs()
+ {
+ return List.of(in, enable);
+ }
+
+ @Override
+ public List<ReadWriteEnd> getAllOutputs()
+ {
+ return List.of(out);
+ }
+
+}
--- /dev/null
+package mograsim.logic.core.components.gates;
+
+import mograsim.logic.core.timeline.Timeline;
+import mograsim.logic.core.types.BitVector.BitVectorMutator;
+import mograsim.logic.core.wires.Wire.ReadEnd;
+import mograsim.logic.core.wires.Wire.ReadWriteEnd;
+
+public class AndGate extends MultiInputGate
+{
+ public AndGate(Timeline timeline, int processTime, ReadWriteEnd out, ReadEnd... in)
+ {
+ super(timeline, processTime, BitVectorMutator::and, out, in);
+ }
+}
--- /dev/null
+package mograsim.logic.core.components.gates;
+
+import java.util.List;
+
+import mograsim.logic.core.components.BasicComponent;
+import mograsim.logic.core.timeline.Timeline;
+import mograsim.logic.core.types.MutationOperation;
+import mograsim.logic.core.types.BitVector.BitVectorMutator;
+import mograsim.logic.core.wires.Wire.ReadEnd;
+import mograsim.logic.core.wires.Wire.ReadWriteEnd;
+
+public abstract class MultiInputGate extends BasicComponent
+{
+ protected ReadEnd[] in;
+ protected ReadWriteEnd out;
+ protected final int length;
+ protected MutationOperation op;
+
+ protected MultiInputGate(Timeline timeline, int processTime, MutationOperation op, ReadWriteEnd out, ReadEnd... in)
+ {
+ super(timeline, processTime);
+ this.op = op;
+ length = out.length();
+ this.in = in.clone();
+ if (in.length < 1)
+ throw new IllegalArgumentException(String.format("Cannot create gate with %d wires.", in.length));
+ for (ReadEnd w : in)
+ {
+ if (w.length() != length)
+ throw new IllegalArgumentException("All wires connected to the gate must be of uniform length.");
+ w.addObserver(this);
+ }
+ this.out = out;
+ }
+
+ @Override
+ public List<ReadEnd> getAllInputs()
+ {
+ return List.of(in);
+ }
+
+ @Override
+ public List<ReadWriteEnd> getAllOutputs()
+ {
+ return List.of(out);
+ }
+
+ @Override
+ protected void compute()
+ {
+ BitVectorMutator mutator = BitVectorMutator.empty();
+ for (ReadEnd w : in)
+ op.apply(mutator, w.getValues());
+ out.feedSignals(mutator.get());
+ }
+}
--- /dev/null
+package mograsim.logic.core.components.gates;
+
+import java.util.List;
+
+import mograsim.logic.core.components.BasicComponent;
+import mograsim.logic.core.timeline.Timeline;
+import mograsim.logic.core.wires.Wire.ReadEnd;
+import mograsim.logic.core.wires.Wire.ReadWriteEnd;
+
+public class NotGate extends BasicComponent
+{
+ private ReadEnd in;
+ private ReadWriteEnd out;
+
+ public NotGate(Timeline timeline, int processTime, ReadEnd in, ReadWriteEnd out)
+ {
+ super(timeline, processTime);
+ this.in = in;
+ in.addObserver(this);
+ this.out = out;
+ }
+
+ @Override
+ protected void compute()
+ {
+ out.feedSignals(in.getValues().not());
+ }
+
+ public ReadEnd getIn()
+ {
+ return in;
+ }
+
+ public ReadEnd getOut()
+ {
+ return out;
+ }
+
+ @Override
+ public List<ReadEnd> getAllInputs()
+ {
+ return List.of(in);
+ }
+
+ @Override
+ public List<ReadWriteEnd> getAllOutputs()
+ {
+ return List.of(out);
+ }
+}
--- /dev/null
+package mograsim.logic.core.components.gates;
+
+import mograsim.logic.core.timeline.Timeline;
+import mograsim.logic.core.types.BitVector.BitVectorMutator;
+import mograsim.logic.core.wires.Wire.ReadEnd;
+import mograsim.logic.core.wires.Wire.ReadWriteEnd;
+
+public class OrGate extends MultiInputGate
+{
+ public OrGate(Timeline timeline, int processTime, ReadWriteEnd out, ReadEnd... in)
+ {
+ super(timeline, processTime, BitVectorMutator::or, out, in);
+ }
+}
--- /dev/null
+package mograsim.logic.core.components.gates;
+
+import mograsim.logic.core.timeline.Timeline;
+import mograsim.logic.core.types.BitVector.BitVectorMutator;
+import mograsim.logic.core.wires.Wire.ReadEnd;
+import mograsim.logic.core.wires.Wire.ReadWriteEnd;
+
+/**
+ * Outputs 1 when the number of 1 inputs is odd.
+ *
+ * @author Fabian Stemmler
+ */
+public class XorGate extends MultiInputGate
+{
+ public XorGate(Timeline timeline, int processTime, ReadWriteEnd out, ReadEnd... in)
+ {
+ super(timeline, processTime, BitVectorMutator::xor, out, in);
+ }
+
+}
--- /dev/null
+package mograsim.logic.core.tests;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.function.LongConsumer;
+
+import org.junit.jupiter.api.Test;
+
+import mograsim.logic.core.components.Connector;
+import mograsim.logic.core.components.Demux;
+import mograsim.logic.core.components.Merger;
+import mograsim.logic.core.components.Mux;
+import mograsim.logic.core.components.Splitter;
+import mograsim.logic.core.components.TriStateBuffer;
+import mograsim.logic.core.components.gates.AndGate;
+import mograsim.logic.core.components.gates.NotGate;
+import mograsim.logic.core.components.gates.OrGate;
+import mograsim.logic.core.components.gates.XorGate;
+import mograsim.logic.core.timeline.Timeline;
+import mograsim.logic.core.types.Bit;
+import mograsim.logic.core.types.BitVector;
+import mograsim.logic.core.wires.Wire;
+import mograsim.logic.core.wires.Wire.ReadEnd;
+import mograsim.logic.core.wires.Wire.ReadWriteEnd;
+
+class ComponentTest
+{
+ private Timeline t = new Timeline(11);
+
+ @Test
+ void circuitExampleTest()
+ {
+ Wire a = new Wire(t, 1, 1), b = new Wire(t, 1, 1), c = new Wire(t, 1, 10), d = new Wire(t, 2, 1), e = new Wire(t, 1, 1),
+ f = new Wire(t, 1, 1), g = new Wire(t, 1, 1), h = new Wire(t, 2, 1), i = new Wire(t, 2, 1), j = new Wire(t, 1, 1),
+ k = new Wire(t, 1, 1);
+ new AndGate(t, 1, f.createReadWriteEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd());
+ new NotGate(t, 1, f.createReadOnlyEnd(), g.createReadWriteEnd());
+ new Merger(t, h.createReadWriteEnd(), c.createReadOnlyEnd(), g.createReadOnlyEnd());
+ new Mux(t, 1, i.createReadWriteEnd(), e.createReadOnlyEnd(), h.createReadOnlyEnd(), d.createReadOnlyEnd());
+ new Splitter(t, i.createReadOnlyEnd(), k.createReadWriteEnd(), j.createReadWriteEnd());
+
+ a.createReadWriteEnd().feedSignals(Bit.ZERO);
+ b.createReadWriteEnd().feedSignals(Bit.ONE);
+ c.createReadWriteEnd().feedSignals(Bit.ZERO);
+ d.createReadWriteEnd().feedSignals(Bit.ONE, Bit.ONE);
+ e.createReadWriteEnd().feedSignals(Bit.ZERO);
+
+ t.executeAll();
+
+ assertEquals(Bit.ONE, j.getValue());
+ assertEquals(Bit.ZERO, k.getValue());
+ }
+
+ @Test
+ void splitterTest()
+ {
+ t.reset();
+ Wire a = new Wire(t, 3, 1), b = new Wire(t, 2, 1), c = new Wire(t, 3, 1), in = new Wire(t, 8, 1);
+ in.createReadWriteEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE);
+ new Splitter(t, in.createReadOnlyEnd(), a.createReadWriteEnd(), b.createReadWriteEnd(), c.createReadWriteEnd());
+
+ t.executeAll();
+
+ assertBitArrayEquals(a.getValues(), Bit.ZERO, Bit.ONE, Bit.ZERO);
+ assertBitArrayEquals(b.getValues(), Bit.ONE, Bit.ZERO);
+ assertBitArrayEquals(c.getValues(), Bit.ONE, Bit.ZERO, Bit.ONE);
+ }
+
+ @Test
+ void mergerTest()
+ {
+ t.reset();
+ Wire a = new Wire(t, 3, 1), b = new Wire(t, 2, 1), c = new Wire(t, 3, 1), out = new Wire(t, 8, 1);
+ a.createReadWriteEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ZERO);
+ b.createReadWriteEnd().feedSignals(Bit.ONE, Bit.ZERO);
+ c.createReadWriteEnd().feedSignals(Bit.ONE, Bit.ZERO, Bit.ONE);
+
+ new Merger(t, out.createReadWriteEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd(), c.createReadOnlyEnd());
+
+ t.executeAll();
+
+ assertBitArrayEquals(out.getValues(), Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE);
+ }
+
+ @Test
+ void triStateBufferTest()
+ {
+ Wire a = new Wire(t, 1, 1), b = new Wire(t, 1, 1), en = new Wire(t, 1, 1), notEn = new Wire(t, 1, 1);
+ new NotGate(t, 1, en.createReadOnlyEnd(), notEn.createReadWriteEnd());
+ new TriStateBuffer(t, 1, a.createReadOnlyEnd(), b.createReadWriteEnd(), en.createReadOnlyEnd());
+ new TriStateBuffer(t, 1, b.createReadOnlyEnd(), a.createReadWriteEnd(), notEn.createReadOnlyEnd());
+
+ ReadWriteEnd enI = en.createReadWriteEnd(), aI = a.createReadWriteEnd(), bI = b.createReadWriteEnd();
+ enI.feedSignals(Bit.ONE);
+ aI.feedSignals(Bit.ONE);
+ bI.feedSignals(Bit.Z);
+
+ t.executeAll();
+
+ assertEquals(Bit.ONE, b.getValue());
+
+ bI.feedSignals(Bit.ZERO);
+
+ t.executeAll();
+
+ assertEquals(Bit.X, b.getValue());
+ assertEquals(Bit.ONE, a.getValue());
+
+ aI.clearSignals();
+ enI.feedSignals(Bit.ZERO);
+
+ t.executeAll();
+
+ assertEquals(Bit.ZERO, a.getValue());
+
+ }
+
+ @Test
+ void muxTest()
+ {
+ t.reset();
+ Wire a = new Wire(t, 4, 3), b = new Wire(t, 4, 6), c = new Wire(t, 4, 4), select = new Wire(t, 2, 5), out = new Wire(t, 4, 1);
+ ReadWriteEnd selectIn = select.createReadWriteEnd();
+
+ selectIn.feedSignals(Bit.ZERO, Bit.ZERO);
+ a.createReadWriteEnd().feedSignals(Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO);
+ c.createReadWriteEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE);
+
+ new Mux(t, 1, out.createReadWriteEnd(), select.createReadOnlyEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd(),
+ c.createReadOnlyEnd());
+ t.executeAll();
+
+ assertBitArrayEquals(out.getValues(), Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO);
+ selectIn.feedSignals(Bit.ZERO, Bit.ONE);
+ t.executeAll();
+
+ assertBitArrayEquals(out.getValues(), Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE);
+
+ selectIn.feedSignals(Bit.ONE, Bit.ONE);
+ t.executeAll();
+
+ assertBitArrayEquals(out.getValues(), Bit.Z, Bit.Z, Bit.Z, Bit.Z);
+
+ }
+
+ @Test
+ void demuxTest()
+ {
+ t.reset();
+ Wire a = new Wire(t, 4, 3), b = new Wire(t, 4, 6), c = new Wire(t, 4, 4), select = new Wire(t, 2, 5), in = new Wire(t, 4, 1);
+ ReadWriteEnd selectIn = select.createReadWriteEnd();
+
+ selectIn.feedSignals(Bit.ZERO, Bit.ZERO);
+ in.createReadWriteEnd().feedSignals(Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO);
+
+ new Demux(t, 1, in.createReadOnlyEnd(), select.createReadOnlyEnd(), a.createReadWriteEnd(), b.createReadWriteEnd(),
+ c.createReadWriteEnd());
+ t.executeAll();
+
+ assertBitArrayEquals(a.getValues(), Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO);
+ assertBitArrayEquals(b.getValues(), Bit.U, Bit.U, Bit.U, Bit.U);
+ assertBitArrayEquals(c.getValues(), Bit.U, Bit.U, Bit.U, Bit.U);
+ selectIn.feedSignals(Bit.ZERO, Bit.ONE);
+ t.executeAll();
+
+ assertBitArrayEquals(a.getValues(), Bit.Z, Bit.Z, Bit.Z, Bit.Z);
+ assertBitArrayEquals(b.getValues(), Bit.U, Bit.U, Bit.U, Bit.U);
+ assertBitArrayEquals(c.getValues(), Bit.ONE, Bit.ZERO, Bit.ONE, Bit.ZERO);
+
+ selectIn.feedSignals(Bit.ONE, Bit.ONE);
+ t.executeAll();
+
+ assertBitArrayEquals(a.getValues(), Bit.Z, Bit.Z, Bit.Z, Bit.Z);
+ assertBitArrayEquals(b.getValues(), Bit.U, Bit.U, Bit.U, Bit.U);
+ assertBitArrayEquals(c.getValues(), Bit.Z, Bit.Z, Bit.Z, Bit.Z);
+
+ }
+
+ @Test
+ void andTest()
+ {
+ t.reset();
+ Wire a = new Wire(t, 4, 1), b = new Wire(t, 4, 3), c = new Wire(t, 4, 1);
+ new AndGate(t, 1, c.createReadWriteEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd());
+ a.createReadWriteEnd().feedSignals(Bit.ONE, Bit.ONE, Bit.ZERO, Bit.ZERO);
+ b.createReadWriteEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE);
+
+ t.executeAll();
+
+ assertBitArrayEquals(c.getValues(), Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ZERO);
+ }
+
+ @Test
+ void orTest()
+ {
+ t.reset();
+ Wire a = new Wire(t, 4, 1), b = new Wire(t, 4, 3), c = new Wire(t, 4, 1);
+ new OrGate(t, 1, c.createReadWriteEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd());
+ a.createReadWriteEnd().feedSignals(Bit.ONE, Bit.ONE, Bit.ZERO, Bit.ZERO);
+ b.createReadWriteEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ZERO, Bit.ONE);
+
+ t.executeAll();
+
+ assertBitArrayEquals(c.getValues(), Bit.ONE, Bit.ONE, Bit.ZERO, Bit.ONE);
+ }
+
+ @Test
+ void xorTest()
+ {
+ t.reset();
+ Wire a = new Wire(t, 3, 1), b = new Wire(t, 3, 2), c = new Wire(t, 3, 1), d = new Wire(t, 3, 1);
+ new XorGate(t, 1, d.createReadWriteEnd(), a.createReadOnlyEnd(), b.createReadOnlyEnd(), c.createReadOnlyEnd());
+ a.createReadWriteEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ONE);
+ b.createReadWriteEnd().feedSignals(Bit.ONE, Bit.ZERO, Bit.ONE);
+ c.createReadWriteEnd().feedSignals(Bit.ONE, Bit.ZERO, Bit.ONE);
+
+ t.executeAll();
+
+ assertBitArrayEquals(d.getValues(), Bit.ZERO, Bit.ONE, Bit.ONE);
+ }
+
+ @Test
+ void notTest()
+ {
+ t.reset();
+ Wire a = new Wire(t, 3, 1), b = new Wire(t, 3, 2);
+ new NotGate(t, 1, a.createReadOnlyEnd(), b.createReadWriteEnd());
+ a.createReadWriteEnd().feedSignals(Bit.ZERO, Bit.ONE, Bit.ONE);
+
+ t.executeAll();
+
+ assertBitArrayEquals(b.getValues(), Bit.ONE, Bit.ZERO, Bit.ZERO);
+ }
+
+ @Test
+ void rsLatchCircuitTest()
+ {
+ t.reset();
+ Wire r = new Wire(t, 1, 1), s = new Wire(t, 1, 1), t1 = new Wire(t, 1, 15), t2 = new Wire(t, 1, 1), q = new Wire(t, 1, 1),
+ nq = new Wire(t, 1, 1);
+
+ new OrGate(t, 1, t2.createReadWriteEnd(), r.createReadOnlyEnd(), nq.createReadOnlyEnd());
+ new OrGate(t, 1, t1.createReadWriteEnd(), s.createReadOnlyEnd(), q.createReadOnlyEnd());
+ new NotGate(t, 1, t2.createReadOnlyEnd(), q.createReadWriteEnd());
+ new NotGate(t, 1, t1.createReadOnlyEnd(), nq.createReadWriteEnd());
+
+ ReadWriteEnd sIn = s.createReadWriteEnd(), rIn = r.createReadWriteEnd();
+
+ sIn.feedSignals(Bit.ONE);
+ rIn.feedSignals(Bit.ZERO);
+
+ t.executeAll();
+
+ assertEquals(Bit.ONE, q.getValue());
+ assertEquals(Bit.ZERO, nq.getValue());
+
+ sIn.feedSignals(Bit.ZERO);
+
+ t.executeAll();
+ assertEquals(Bit.ONE, q.getValue());
+ assertEquals(Bit.ZERO, nq.getValue());
+
+ rIn.feedSignals(Bit.ONE);
+
+ t.executeAll();
+
+ assertEquals(Bit.ZERO, q.getValue());
+ assertEquals(Bit.ONE, nq.getValue());
+ }
+
+ @Test
+ void numericValueTest()
+ {
+ t.reset();
+
+ Wire a = new Wire(t, 4, 1);
+ a.createReadWriteEnd().feedSignals(Bit.ONE, Bit.ONE, Bit.ONE, Bit.ONE);
+
+ t.executeAll();
+
+ assertEquals(15, a.getUnsignedValue());
+ assertEquals(-1, a.getSignedValue());
+ }
+
+ boolean flag = false;
+
+ @Test
+ void simpleTimelineTest()
+ {
+ Timeline t = new Timeline(3);
+ flag = false;
+ t.addEvent((e) ->
+ {
+ if (!flag)
+ fail();
+ flag = false;
+ }, 15);
+ t.addEvent((e) ->
+ {
+ if (flag)
+ fail();
+ flag = true;
+ }, 10);
+ t.addEvent((e) ->
+ {
+ if (flag)
+ fail();
+ flag = true;
+ }, 20);
+ t.addEvent((e) ->
+ {
+ fail("Only supposed to execute until timestamp 20, not 25");
+ }, 25);
+
+ t.executeUntil(t.laterThan(20), 100);
+
+ if (!flag)
+ fail();
+ }
+
+ @Test
+ void multipleInputs()
+ {
+ t.reset();
+ Wire w = new Wire(t, 2, 1);
+ ReadWriteEnd wI1 = w.createReadWriteEnd(), wI2 = w.createReadWriteEnd();
+ wI1.feedSignals(Bit.ONE, Bit.Z);
+ wI2.feedSignals(Bit.Z, Bit.X);
+ t.executeAll();
+ assertBitArrayEquals(w.getValues(), Bit.ONE, Bit.X);
+
+ wI2.feedSignals(Bit.ZERO, Bit.Z);
+ t.executeAll();
+ assertBitArrayEquals(w.getValues(), Bit.X, Bit.Z);
+
+ wI2.feedSignals(Bit.Z, Bit.Z);
+ t.executeAll();
+ assertBitArrayEquals(w.getValues(), Bit.ONE, Bit.Z);
+
+ wI2.feedSignals(Bit.ONE, Bit.Z);
+ ReadEnd rE = w.createReadOnlyEnd();
+ rE.addObserver((i, oldValues) -> fail("WireEnd notified observer, although value did not change."));
+ t.executeAll();
+ rE.close();
+ wI1.feedSignals(Bit.X, Bit.X);
+ t.executeAll();
+ wI1.addObserver((i, oldValues) -> fail("WireEnd notified observer, although it was closed."));
+ wI1.close();
+ assertBitArrayEquals(w.getValues(), Bit.ONE, Bit.Z);
+ }
+
+ @Test
+ void wireConnections()
+ {
+ // Nur ein Experiment, was über mehrere 'passive' Bausteine hinweg passieren würde
+
+ t.reset();
+
+ Wire a = new Wire(t, 1, 2);
+ Wire b = new Wire(t, 1, 2);
+ Wire c = new Wire(t, 1, 2);
+ ReadWriteEnd aI = a.createReadWriteEnd();
+ ReadWriteEnd bI = b.createReadWriteEnd();
+ ReadWriteEnd cI = c.createReadWriteEnd();
+
+ TestBitDisplay test = new TestBitDisplay(t, c.createReadOnlyEnd());
+ TestBitDisplay test2 = new TestBitDisplay(t, a.createReadOnlyEnd());
+ LongConsumer print = time -> System.out.format("Time %2d\n a: %s\n b: %s\n c: %s\n", time, a, b, c);
+
+ cI.feedSignals(Bit.ONE);
+ test.assertAfterSimulationIs(print, Bit.ONE);
+
+ cI.feedSignals(Bit.X);
+ test.assertAfterSimulationIs(print, Bit.X);
+
+ cI.feedSignals(Bit.X);
+ cI.feedSignals(Bit.Z);
+ test.assertAfterSimulationIs(print, Bit.Z);
+
+ new Connector(t, b.createReadWriteEnd(), c.createReadWriteEnd()).connect();
+ test.assertAfterSimulationIs(print, Bit.Z);
+ System.err.println("ONE");
+ bI.feedSignals(Bit.ONE);
+ test.assertAfterSimulationIs(print, Bit.ONE);
+ System.err.println("ZERO");
+ bI.feedSignals(Bit.ZERO);
+ test.assertAfterSimulationIs(print, Bit.ZERO);
+ System.err.println("Z");
+ bI.feedSignals(Bit.Z);
+ test.assertAfterSimulationIs(print, Bit.Z);
+
+ new Connector(t, a.createReadWriteEnd(), b.createReadWriteEnd()).connect();
+ System.err.println("Z 2");
+ aI.feedSignals(Bit.Z);
+ test.assertAfterSimulationIs(print, Bit.Z);
+ test2.assertAfterSimulationIs(Bit.Z);
+ System.err.println("ONE 2");
+ aI.feedSignals(Bit.ONE);
+ test.assertAfterSimulationIs(print, Bit.ONE);
+ test2.assertAfterSimulationIs(Bit.ONE);
+ System.err.println("ZERO 2");
+ aI.feedSignals(Bit.ZERO);
+ test.assertAfterSimulationIs(print, Bit.ZERO);
+ test2.assertAfterSimulationIs(Bit.ZERO);
+ System.err.println("Z 2 II");
+ aI.feedSignals(Bit.Z);
+ test.assertAfterSimulationIs(print, Bit.Z);
+ test2.assertAfterSimulationIs(Bit.Z);
+
+ System.err.println("No Conflict yet");
+ bI.feedSignals(Bit.ONE);
+ test.assertAfterSimulationIs(print, Bit.ONE);
+ test2.assertAfterSimulationIs(Bit.ONE);
+ aI.feedSignals(Bit.ONE);
+ test.assertAfterSimulationIs(print, Bit.ONE);
+ test2.assertAfterSimulationIs(Bit.ONE);
+ System.err.println("Conflict");
+ aI.feedSignals(Bit.ZERO);
+ test.assertAfterSimulationIs(print, Bit.X);
+ test2.assertAfterSimulationIs(Bit.X);
+ aI.feedSignals(Bit.ONE);
+ test.assertAfterSimulationIs(print, Bit.ONE);
+ test2.assertAfterSimulationIs(Bit.ONE);
+ }
+
+ private static void assertBitArrayEquals(BitVector actual, Bit... expected)
+ {
+ assertArrayEquals(expected, actual.getBits());
+ }
+}
--- /dev/null
+package mograsim.logic.core.tests;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.WindowConstants;
+
+import mograsim.logic.core.components.ManualSwitch;
+import mograsim.logic.core.components.gates.NotGate;
+import mograsim.logic.core.components.gates.OrGate;
+import mograsim.logic.core.timeline.Timeline;
+import mograsim.logic.core.timeline.Timeline.ExecutionResult;
+import mograsim.logic.core.wires.Wire;
+
+public class GUITest extends JPanel
+{
+
+ private static final long serialVersionUID = 1L;
+
+ private static final int WIRE_DELAY = 40;
+ private static final int OR_DELAY = 100;
+ private static final int NOT_DELAY = 100;
+
+ private Timeline t = new Timeline(11);
+
+ Wire r = new Wire(t, 1, WIRE_DELAY);
+ Wire s = new Wire(t, 1, WIRE_DELAY);
+ Wire t1 = new Wire(t, 1, WIRE_DELAY);
+ Wire t2 = new Wire(t, 1, WIRE_DELAY);
+ Wire q = new Wire(t, 1, WIRE_DELAY);
+ Wire nq = new Wire(t, 1, WIRE_DELAY);
+
+ ManualSwitch rIn = new ManualSwitch(t, r.createReadWriteEnd());
+ ManualSwitch sIn = new ManualSwitch(t, s.createReadWriteEnd());
+
+ OrGate or1 = new OrGate(t, OR_DELAY, t2.createReadWriteEnd(), r.createReadOnlyEnd(), nq.createReadOnlyEnd());
+ OrGate or2 = new OrGate(t, OR_DELAY, t1.createReadWriteEnd(), s.createReadOnlyEnd(), q.createReadOnlyEnd());
+ NotGate not1 = new NotGate(t, NOT_DELAY, t2.createReadOnlyEnd(), q.createReadWriteEnd());
+ NotGate not2 = new NotGate(t, NOT_DELAY, t1.createReadOnlyEnd(), nq.createReadWriteEnd());
+
+ Map<ManualSwitch, Rectangle> switchMap = new HashMap<>();
+
+ int height;
+ int width;
+ boolean sizeChanged;
+
+ public GUITest()
+ {
+ addMouseListener(new MouseListener()
+ {
+
+ @Override
+ public void mouseReleased(MouseEvent e)
+ {
+ for (Entry<ManualSwitch, Rectangle> dim : switchMap.entrySet())
+ {
+ if (dim.getValue().contains(e.getPoint()))
+ {
+ dim.getKey().switchOff();
+ repaint();
+ }
+ }
+ }
+
+ @Override
+ public void mousePressed(MouseEvent e)
+ {
+ for (Entry<ManualSwitch, Rectangle> dim : switchMap.entrySet())
+ {
+ if (dim.getValue().contains(e.getPoint()))
+ {
+ dim.getKey().switchOn();
+ repaint();
+ }
+ }
+ }
+
+ @Override
+ public void mouseExited(MouseEvent e)
+ {
+ // none
+ }
+
+ @Override
+ public void mouseEntered(MouseEvent e)
+ {
+ // none
+ }
+
+ @Override
+ public void mouseClicked(MouseEvent e)
+ {
+ // If you want toggle buttons, use this code instead
+// for (Entry<ManualSwitch, Rectangle> dim : switchMap.entrySet()) {
+// if (dim.getValue().contains(e.getPoint())) {
+// dim.getKey().toggle();
+// repaint();
+// }
+// }
+ }
+ });
+ }
+
+ public Timeline getTimeline()
+ {
+ return t;
+ };
+
+ @Override
+ public void paint(Graphics some_g)
+ {
+ super.paint(some_g);
+ Graphics2D g = ((Graphics2D) some_g);
+ g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_GASP);
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
+
+ checkSizeChange();
+ adaptFont(g);
+
+ drawWire(g, r, "r", 2, 9, 4, 9);
+
+ drawWire(g, s, "s", 2, 3, 4, 3);
+
+ drawWire(g, t2, "t2", 5, 8.5, 6, 8.5);
+
+ drawWire(g, t1, "t1", 5, 3.5, 6, 3.5);
+
+ drawWire(g, q, "q", 7, 8.5, 9, 8.5);
+
+ drawWire(g, nq, "nq", 7, 3.5, 9, 3.5);
+
+ drawWire(g, q, "", 7.5, 8.5, 7.5, 7.5);
+ drawWire(g, q, "", 7.5, 7.5, 3, 4.5);
+ drawWire(g, q, "", 3, 4.5, 3, 4);
+ drawWire(g, q, "q", 3, 4, 4, 4);
+
+ drawWire(g, nq, "", 7.5, 3.5, 7.5, 4.5);
+ drawWire(g, nq, "", 7.5, 4.5, 3, 7.5);
+ drawWire(g, nq, "", 3, 7.5, 3, 8);
+ drawWire(g, nq, "nq", 3, 8, 4, 8);
+
+ drawSquare(g, 4, 8, "OR");
+ drawSquare(g, 4, 3, "OR");
+
+ drawSquare(g, 6, 8, "NOT");
+ drawSquare(g, 6, 3, "NOT");
+
+ drawSwitch(g, rIn, "Switch R", 0.5, 8.25, 2, 9.75);
+ drawSwitch(g, sIn, "Switch S", 0.5, 2.25, 2, 3.75);
+
+ drawString(g, "Hint: drag the cursor out of the pressed switch to keep it's state", 5, 0, 0.0, 1.0);
+ }
+
+ private void checkSizeChange()
+ {
+ sizeChanged = height != getHeight() || width != getWidth();
+ if (sizeChanged)
+ {
+ height = getHeight();
+ width = getWidth();
+ }
+ }
+
+ private void adaptFont(Graphics g)
+ {
+ g.setFont(g.getFont().deriveFont(Math.min(height, width) / 40f));
+ }
+
+ private void drawString(Graphics g, String s, int x, int y, double anchorX, double anchorY)
+ {
+ int h = g.getFontMetrics().getAscent();
+ int w = g.getFontMetrics().stringWidth(s);
+ g.drawString(s, x - (int) (w * anchorX), y + (int) (h * anchorY));
+ }
+
+ private void drawWire(Graphics g, Wire wa, String name, double x1, double y1, double x2, double y2)
+ {
+ setTo(g, wa);
+ g.drawLine(gX(x1), gY(y1), gX(x2), gY(y2));
+ drawString(g, name, (gX(x1) + gX(x2)) / 2, (gY(y1) + gY(y2)) / 2 - 5, 0, 0);
+ }
+
+ private void drawSquare(Graphics g, int posX, int posY, String text)
+ {
+ int x1 = gX(posX) - 5;
+ int x2 = gX(posX + 1) + 5;
+ int y1 = gY(posY) - 5;
+ int y2 = gY(posY + 1) + 5;
+
+ g.setColor(Color.WHITE);
+ g.fillRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
+ setBlack(g);
+ g.drawRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
+ drawString(g, text, (x1 + x2) / 2, (y1 + y2) / 2, 0.5, 0.5);
+
+ }
+
+ private void drawSwitch(Graphics g, ManualSwitch ms, String text, double posX1, double posY1, double posX2, double posY2)
+ {
+ int x1 = gX(posX1) - 5;
+ int x2 = gX(posX2) + 5;
+ int y1 = gY(posY1) - 5;
+ int y2 = gY(posY2) + 5;
+
+ if (sizeChanged)
+ {
+ Rectangle r = new Rectangle(x1, y1, x2 - x1, y2 - y1);
+ switchMap.put(ms, r);
+ }
+
+ g.setColor(ms.isOn() ? Color.getHSBColor(.3f, .5f, 1f) : Color.WHITE);
+ g.fillRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
+ setBlack(g);
+ g.drawRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
+ drawString(g, text, (x1 + x2) / 2, (y1 + y2) / 2, 0.5, 0.5);
+ }
+
+ private static void setBlack(Graphics g)
+ {
+ g.setColor(Color.BLACK);
+ }
+
+ private static void setTo(Graphics g, Wire wa)
+ {
+ switch (wa.getValue())
+ {
+ case ONE:
+ g.setColor(Color.GREEN);
+ break;
+ case X:
+ g.setColor(Color.RED);
+ break;
+ case Z:
+ g.setColor(Color.DARK_GRAY);
+ break;
+ case ZERO:
+ g.setColor(Color.BLACK);
+ break;
+ case U:
+ g.setColor(Color.MAGENTA);
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
+ private int gY(double pos)
+ {
+ return (int) (pos * height / 11);
+ }
+
+ private int gX(double pos)
+ {
+ return (int) (pos * width / 11) + 50;
+ }
+
+ public static void main(String[] args)
+ {
+ JFrame f = new JFrame("Test circuit 1.0.0");
+ GUITest gt = new GUITest();
+ f.add(gt);
+ f.setSize(800, 600);
+ f.setLocation(500, 400);
+ f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
+ f.setVisible(true);
+
+ long begin = System.currentTimeMillis();
+
+ long lastFrame = begin;
+ long updateT = 16;
+
+ while (f.isVisible())
+ {
+ ExecutionResult er = gt.getTimeline().executeUntil(gt.getTimeline().laterThan((lastFrame - begin) * 3), lastFrame + 14);
+// if (t.hasNext())
+// t.executeNext();
+ if (er != ExecutionResult.NOTHING_DONE)
+ gt.repaint(12);
+ try
+ {
+ Thread.sleep(Math.max(updateT - System.currentTimeMillis() + lastFrame, 0));
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ lastFrame = System.currentTimeMillis();
+ }
+ }
+}
--- /dev/null
+package mograsim.logic.core.tests;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+
+import java.util.function.LongConsumer;
+
+import mograsim.logic.core.components.BitDisplay;
+import mograsim.logic.core.timeline.Timeline;
+import mograsim.logic.core.types.Bit;
+import mograsim.logic.core.wires.Wire.ReadEnd;
+
+public final class TestBitDisplay extends BitDisplay
+{
+
+ public TestBitDisplay(Timeline timeline, ReadEnd in)
+ {
+ super(timeline, in);
+ }
+
+ public void assertDisplays(Bit... expected)
+ {
+ assertArrayEquals(expected, getDisplayedValue().getBits());
+ }
+
+ public void assertAfterSimulationIs(Bit... expected)
+ {
+ timeline.executeAll();
+ assertDisplays(expected);
+ }
+
+ public void assertAfterSimulationIs(LongConsumer r, Bit... expected)
+ {
+ while (timeline.hasNext())
+ {
+ timeline.executeNext();
+ r.accept(timeline.getSimulationTime());
+ }
+ assertDisplays(expected);
+ }
+
+ @Override
+ protected void compute()
+ {
+ super.compute();
+ System.out.println("update: value is " + getDisplayedValue());
+ }
+}
--- /dev/null
+package mograsim.logic.core.timeline;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.PriorityQueue;
+import java.util.function.BooleanSupplier;
+import java.util.function.Consumer;
+import java.util.function.LongSupplier;
+
+/**
+ * Orders Events by the time they are due to be executed. Can execute Events individually.
+ *
+ * @author Fabian Stemmler
+ *
+ */
+public class Timeline
+{
+ private PriorityQueue<InnerEvent> events;
+ private LongSupplier time;
+ private long lastTimeUpdated = 0;
+
+ private final List<Consumer<TimelineEvent>> eventAddedListener;
+
+ public Timeline(int initCapacity)
+ {
+ events = new PriorityQueue<InnerEvent>(initCapacity);
+
+ eventAddedListener = new ArrayList<>();
+ time = () -> lastTimeUpdated;
+ }
+
+ /**
+ * @param timestamp exclusive
+ * @return true if the first event is later than the timestamp
+ */
+ public BooleanSupplier laterThan(long timestamp)
+ {
+ return () -> timeCmp(events.peek().getTiming(), timestamp) > 0;
+ }
+
+ public boolean hasNext()
+ {
+ return !events.isEmpty();
+ }
+
+ /**
+ * Executes all events at the next timestamp, at which there are any
+ */
+ public void executeNext()
+ {
+ InnerEvent first = events.peek();
+ if (first != null)
+ executeUntil(laterThan(first.getTiming()), -1);
+ }
+
+ public void executeAll()
+ {
+ while (hasNext())
+ executeNext();
+ }
+
+ /**
+ * Executes all events until a given condition is met. The simulation process can be constrained by a real world timestamp.
+ *
+ * @param condition the condition until which the events are be processed
+ * @param stopMillis the System.currentTimeMillis() when simulation definitely needs to stop. A value of -1 means no timeout.
+ * @return State of the event execution
+ * @formatter:off
+ * <code>NOTHING_DONE</code> if the {@link Timeline} was already empty
+ * <code>EXEC_OUT_OF_TIME</code> if the given maximum time was reached
+ * <code>EXEC_UNTIL_CONDITION</code> if the condition was met
+ * <code>EXEC_UNTIL_EMPTY</code> if events were executed until the {@link Timeline} was empty
+ * @formatter:on
+ * @author Christian Femers, Fabian Stemmler
+ */
+ public ExecutionResult executeUntil(BooleanSupplier condition, long stopMillis)
+ {
+ if (events.isEmpty())
+ {
+ lastTimeUpdated = getSimulationTime();
+ return ExecutionResult.NOTHING_DONE;
+ }
+ int checkStop = 0;
+ InnerEvent first = events.peek();
+ while (hasNext() && !condition.getAsBoolean())
+ {
+ events.remove();
+ lastTimeUpdated = first.getTiming();
+ first.run();
+ // Don't check after every run
+ checkStop = (checkStop + 1) % 10;
+ if (checkStop == 0 && System.currentTimeMillis() >= stopMillis)
+ return ExecutionResult.EXEC_OUT_OF_TIME;
+ first = events.peek();
+ }
+ lastTimeUpdated = getSimulationTime();
+ return hasNext() ? ExecutionResult.EXEC_UNTIL_EMPTY : ExecutionResult.EXEC_UNTIL_CONDITION;
+ }
+
+ public void setTimeFunction(LongSupplier time)
+ {
+ this.time = time;
+ }
+
+ public long getSimulationTime()
+ {
+ return time.getAsLong();
+ }
+
+ public long nextEventTime()
+ {
+ if (!hasNext())
+ return -1;
+ return events.peek().getTiming();
+ }
+
+ public void reset()
+ {
+ events.clear();
+ lastTimeUpdated = 0;
+ }
+
+ public void addEventAddedListener(Consumer<TimelineEvent> listener)
+ {
+ eventAddedListener.add(listener);
+ }
+
+ public void removeEventAddedListener(Consumer<TimelineEvent> listener)
+ {
+ eventAddedListener.remove(listener);
+ }
+
+ /**
+ * Adds an Event to the {@link Timeline}
+ *
+ * @param function The {@link TimelineEventHandler} that will be executed, when the {@link InnerEvent} occurs on the timeline.
+ * @param relativeTiming The amount of MI ticks in which the {@link InnerEvent} is called, starting from the current time.
+ */
+ public void addEvent(TimelineEventHandler function, int relativeTiming)
+ {
+ long timing = getSimulationTime() + relativeTiming;
+ TimelineEvent event = new TimelineEvent(timing);
+ events.add(new InnerEvent(function, event));
+ eventAddedListener.forEach(l -> l.accept(event));
+ }
+
+ private class InnerEvent implements Runnable, Comparable<InnerEvent>
+ {
+ private final TimelineEventHandler function;
+ private final TimelineEvent event;
+
+ /**
+ * Creates an {@link InnerEvent}
+ *
+ * @param function {@link TimelineEventHandler} to be executed when the {@link InnerEvent} occurs
+ * @param timing Point in the MI simulation {@link Timeline}, at which the {@link InnerEvent} is executed;
+ */
+ InnerEvent(TimelineEventHandler function, TimelineEvent event)
+ {
+ this.function = function;
+ this.event = event;
+ }
+
+ public long getTiming()
+ {
+ return event.getTiming();
+ }
+
+ @Override
+ public void run()
+ {
+ function.handle(event);
+ }
+
+ @Override
+ public String toString()
+ {
+ return event.toString();
+ }
+
+ @Override
+ public int compareTo(InnerEvent o)
+ {
+ return timeCmp(getTiming(), o.getTiming());
+ }
+ }
+
+ public static int timeCmp(long a, long b)
+ {
+ return Long.signum(a - b);
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("Simulation time: %s, Last update: %d, Events: %s", getSimulationTime(), lastTimeUpdated, events.toString());
+ }
+
+ public enum ExecutionResult
+ {
+ NOTHING_DONE, EXEC_UNTIL_EMPTY, EXEC_UNTIL_CONDITION, EXEC_OUT_OF_TIME
+ }
+}
\ No newline at end of file
--- /dev/null
+package mograsim.logic.core.timeline;
+
+/**
+ * A class that stores all relevant information about an event in the {@link Timeline}. Currently, there is not much relevant information to
+ * store.
+ *
+ * @author Fabian Stemmler
+ *
+ */
+public class TimelineEvent
+{
+ private final long timing;
+
+ TimelineEvent(long timing)
+ {
+ super();
+ this.timing = timing;
+ }
+
+ public long getTiming()
+ {
+ return timing;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "timestamp: " + timing;
+ }
+}
\ No newline at end of file
--- /dev/null
+package mograsim.logic.core.timeline;
+
+@FunctionalInterface
+public interface TimelineEventHandler
+{
+ public void handle(TimelineEvent e);
+}
\ No newline at end of file
--- /dev/null
+package mograsim.logic.core.types;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * stdlogic according to IEEE 1164
+ */
+public enum Bit implements StrictLogicType<Bit>
+{
+ U("U"), X("X"), ZERO("0"), ONE("1"), Z("Z");
+
+ private final String symbol;
+
+ private Bit(String symbol)
+ {
+ this.symbol = symbol;
+ }
+
+ @Override
+ public Bit and(Bit other)
+ {
+ return fromTable(AND_TABLE, this, other);
+ }
+
+ @Override
+ public Bit or(Bit other)
+ {
+ return fromTable(OR_TABLE, this, other);
+ }
+
+ @Override
+ public Bit xor(Bit other)
+ {
+ return fromTable(XOR_TABLE, this, other);
+ }
+
+ @Override
+ public Bit not()
+ {
+ switch (this)
+ {
+ case U:
+ return U;
+ case ONE:
+ return ZERO;
+ case ZERO:
+ return ONE;
+ default:
+ return X;
+ }
+ }
+
+ public Bit[] makeArray(int length)
+ {
+ Bit[] bits = new Bit[length];
+ Arrays.fill(bits, this);
+ return bits;
+ }
+
+ public BitVector toVector(int length)
+ {
+ return BitVector.of(this, length);
+ }
+
+ @Override
+ public Bit join(Bit other)
+ {
+ return fromTable(JOIN_TABLE, this, other);
+ }
+
+ @Override
+ public String toString()
+ {
+ return getSymbol();
+ }
+
+ public String getSymbol()
+ {
+ return symbol;
+ }
+
+ public static Bit parse(String s)
+ {
+ Bit bit = SYMBOL_MAP.get(s);
+ Objects.requireNonNull(bit, "No Bit found for symbol " + s);
+ return bit;
+ }
+
+ public static Bit parse(String s, int symbolPosition)
+ {
+ return parse(s.substring(symbolPosition, symbolPosition + 1));
+ }
+
+ private static Bit fromTable(Bit[][] table, Bit a, Bit b)
+ {
+ return table[a.ordinal()][b.ordinal()];
+ }
+
+ static final Map<String, Bit> SYMBOL_MAP = Map.of(U.symbol, U, X.symbol, X, ZERO.symbol, ZERO, ONE.symbol, ONE, Z.symbol, Z);
+
+ // @formatter:off
+ private static final Bit[][] JOIN_TABLE =
+ { { U, U, U, U, U },
+ { U, X, X, X, X },
+ { U, X, ZERO, X, ZERO },
+ { U, X, X, ONE, ONE },
+ { U, X, ZERO, ONE, Z } };
+
+ private static final Bit[][] AND_TABLE =
+ { { U, U, ZERO, U, U },
+ { U, X, ZERO, X, X },
+ { ZERO, ZERO, ZERO, ZERO, ZERO },
+ { U, X, ZERO, ONE, X },
+ { U, X, ZERO, X, X } };
+
+ private static final Bit[][] OR_TABLE =
+ { { U, U, U, ONE, U },
+ { U, X, X, ONE, X },
+ { U, X, ZERO, ONE, X },
+ { ONE, ONE, ONE, ONE, ONE },
+ { U, X, X, ONE, X } };
+
+ private static final Bit[][] XOR_TABLE =
+ { { U, U, U, U, U },
+ { U, X, X, X, X },
+ { U, X, ZERO, ONE, X },
+ { U, X, ONE, ZERO, X },
+ { U, X, X, X, X } };
+ // @formatter:on
+}
\ No newline at end of file
--- /dev/null
+package mograsim.logic.core.types;
+
+import static java.lang.String.format;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.RandomAccess;
+import java.util.function.BinaryOperator;
+import java.util.function.UnaryOperator;
+
+/**
+ * Immutable class representing a {@link Bit}Vector
+ *
+ *
+ * @author Christian Femers
+ *
+ */
+public final class BitVector implements StrictLogicType<BitVector>, Iterable<Bit>, RandomAccess
+{
+ private final Bit[] bits;
+
+ private BitVector(Bit[] bits)
+ {
+ this.bits = Objects.requireNonNull(bits);
+ }
+
+ public static BitVector of(Bit... bits)
+ {
+ return new BitVector(bits.clone());
+ }
+
+ public static BitVector of(Bit bit, int length)
+ {
+ return new BitVector(bit.makeArray(length));
+ }
+
+ public BitVectorMutator mutator()
+ {
+ return BitVectorMutator.of(this);
+ }
+
+ public Bit getBit(int bitIndex)
+ {
+ return bits[bitIndex];
+ }
+
+ public Bit[] getBits()
+ {
+ return bits.clone();
+ }
+
+ @Override
+ public BitVector join(BitVector t)
+ {
+ checkCompatibility(t);
+ return new BitVector(binOp(bits.clone(), t.bits, Bit::join));
+ }
+
+ @Override
+ public BitVector and(BitVector t)
+ {
+ checkCompatibility(t);
+ return new BitVector(binOp(bits.clone(), t.bits, Bit::and));
+ }
+
+ @Override
+ public BitVector or(BitVector t)
+ {
+ checkCompatibility(t);
+ return new BitVector(binOp(bits.clone(), t.bits, Bit::or));
+ }
+
+ @Override
+ public BitVector xor(BitVector t)
+ {
+ checkCompatibility(t);
+ return new BitVector(binOp(bits.clone(), t.bits, Bit::xor));
+ }
+
+ @Override
+ public BitVector not()
+ {
+ return new BitVector(unOp(bits.clone(), Bit::not));
+ }
+
+ public int length()
+ {
+ return bits.length;
+ }
+
+ public BitVector concat(BitVector other)
+ {
+ Bit[] newBits = Arrays.copyOf(bits, length() + other.length());
+ System.arraycopy(other.bits, 0, newBits, length(), other.length());
+ return new BitVector(newBits);
+ }
+
+ public BitVector subVector(int start)
+ {
+ return new BitVector(Arrays.copyOfRange(bits, start, length()));
+ }
+
+ public BitVector subVector(int start, int end)
+ {
+ return new BitVector(Arrays.copyOfRange(bits, start, end));
+ }
+
+ private void checkCompatibility(BitVector bv)
+ {
+ if (length() != bv.length())
+ throw new IllegalArgumentException(format("BitVector length does not match: %d and %d", length(), bv.length()));
+ }
+
+ static Bit[] binOp(Bit[] dest, Bit[] second, BinaryOperator<Bit> op)
+ {
+ if (dest == null)
+ return second.clone();
+ for (int i = 0; i < dest.length; i++)
+ {
+ dest[i] = op.apply(dest[i], second[i]);
+ }
+ return dest;
+ }
+
+ static Bit[] unOp(Bit[] dest, UnaryOperator<Bit> op)
+ {
+ if (dest == null)
+ return null;
+ for (int i = 0; i < dest.length; i++)
+ {
+ dest[i] = op.apply(dest[i]);
+ }
+ return dest;
+ }
+
+ /**
+ * Class for comfortable and efficient manipulation of {@link BitVector}s, similar to {@link StringBuilder}
+ *
+ * @author Christian Femers
+ */
+ @SuppressWarnings("synthetic-access")
+ public static final class BitVectorMutator implements LogicType<BitVectorMutator, BitVector>
+ {
+ private Bit[] bits;
+
+ private BitVectorMutator(Bit[] bits)
+ {
+ this.bits = bits;
+ }
+
+ static BitVectorMutator of(BitVector bv)
+ {
+ return new BitVectorMutator(bv.getBits());
+ }
+
+ /**
+ * Returns an empty mutator which has no bits set and will simply copy the values from the first binary operation performed.
+ *
+ */
+ public static BitVectorMutator empty()
+ {
+ return new BitVectorMutator(null);
+ }
+
+ /**
+ * Produces the resulting, immutable {@link BitVector}<br>
+ *
+ * @throws IllegalStateException if the mutator is (still) empty
+ */
+ public BitVector get()
+ {
+ if (bits == null)
+ throw new IllegalStateException("cannot create a BitVector from an empty mutator");
+ return new BitVector(bits);
+ }
+
+ @Override
+ public BitVectorMutator join(BitVector t)
+ {
+ checkCompatibility(t);
+ bits = binOp(bits, t.bits, Bit::join);
+ return this;
+ }
+
+ @Override
+ public BitVectorMutator and(BitVector t)
+ {
+ checkCompatibility(t);
+ bits = binOp(bits, t.bits, Bit::and);
+ return this;
+ }
+
+ @Override
+ public BitVectorMutator or(BitVector t)
+ {
+ checkCompatibility(t);
+ bits = binOp(bits, t.bits, Bit::or);
+ return this;
+ }
+
+ @Override
+ public BitVectorMutator xor(BitVector t)
+ {
+ checkCompatibility(t);
+ bits = binOp(bits, t.bits, Bit::xor);
+ return this;
+ }
+
+ @Override
+ public BitVectorMutator not()
+ {
+ unOp(bits, Bit::not);
+ return this;
+ }
+
+ private void checkCompatibility(BitVector bv)
+ {
+ if (bits != null && bits.length != bv.length())
+ throw new IllegalArgumentException(format("BitVector length does not match: %d and %d", bits.length, bv.length()));
+ }
+ }
+
+ /**
+ * @see Arrays#hashCode(Object[])
+ */
+ @Override
+ public int hashCode()
+ {
+ return Arrays.hashCode(bits);
+ }
+
+ /**
+ * Does test for equality of values/content
+ *
+ * @see Object#equals(Object)
+ */
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ return true;
+ if (!(obj instanceof BitVector))
+ return false;
+ BitVector other = (BitVector) obj;
+ return Arrays.equals(bits, other.bits);
+ }
+
+ /**
+ * Does test for equality of values/content, shifting the other BitVector by <code>offset</code> to the right.<br>
+ * Therefore <code>offset + other.length() <= this.length()</code> needs to be true.
+ *
+ * @throws ArrayIndexOutOfBoundsException if <code>offset + other.length() > this.length()</code>
+ *
+ * @see Object#equals(Object)
+ */
+ public boolean equalsWithOffset(BitVector other, int offset)
+ {
+ if (other == null)
+ return false;
+ return Arrays.equals(bits, offset, offset + other.length(), other.bits, 0, other.length());
+ }
+
+ /**
+ * All {@link Bit}s symbols concatenated together
+ *
+ * @see #parse(String)
+ */
+ @Override
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder(bits.length);
+ for (Bit bit : bits)
+ sb.append(bit);
+ return sb.toString();
+ }
+
+ /**
+ * Parses a String containing solely {@link Bit} symbols
+ *
+ * @see #toString()
+ */
+ public static BitVector parse(String s)
+ {
+ Bit[] values = new Bit[s.length()];
+ for (int i = 0; i < s.length(); i++)
+ {
+ values[i] = Bit.parse(s, i);
+ }
+ return new BitVector(values);
+ }
+
+ @Override
+ public Iterator<Bit> iterator()
+ {
+ return new Iterator<>()
+ {
+ private int pos = 0;
+
+ @Override
+ public Bit next()
+ {
+ if (!hasNext())
+ throw new NoSuchElementException();
+ return getBit(pos++);
+ }
+
+ @Override
+ public boolean hasNext()
+ {
+ return pos != length();
+ }
+ };
+ }
+}
--- /dev/null
+package mograsim.logic.core.types;
+
+import mograsim.logic.core.types.ColorDefinition.BuiltInColor;
+import mograsim.logic.core.wires.Wire.ReadEnd;
+
+public class BitVectorFormatter
+{
+ public static String formatValueAsString(ReadEnd end)
+ {
+ return formatAsString(end == null ? null : end.getValues());
+ }
+
+ public static String formatAsString(BitVector bitVector)
+ {
+ if (bitVector == null)
+ return "null";
+ else
+ return bitVector.toString();
+ }
+
+ public static ColorDefinition formatAsColor(ReadEnd end)
+ {
+ return formatAsColor(end == null ? null : end.getValues());
+ }
+
+ public static ColorDefinition formatAsColor(BitVector bitVector)
+ {
+ // TODO maybe find a color assignment for multiple-bit bit vectors?
+ if (bitVector == null || bitVector.length() != 1)
+ return new ColorDefinition(BuiltInColor.COLOR_BLACK);
+ else
+ switch (bitVector.getBit(0))
+ {
+ case ONE:
+ return new ColorDefinition(BuiltInColor.COLOR_GREEN);
+ case U:
+ return new ColorDefinition(BuiltInColor.COLOR_CYAN);
+ case X:
+ return new ColorDefinition(BuiltInColor.COLOR_RED);
+ case Z:
+ return new ColorDefinition(BuiltInColor.COLOR_YELLOW);
+ case ZERO:
+ return new ColorDefinition(BuiltInColor.COLOR_GRAY);
+ default:
+ throw new IllegalArgumentException("Unknown enum constant: " + bitVector.getBit(0));
+ }
+ }
+
+ private BitVectorFormatter()
+ {
+ throw new UnsupportedOperationException("No BitVectorFormatter instances");
+ }
+}
\ No newline at end of file
--- /dev/null
+package mograsim.logic.core.types;
+
+/**
+ * A way to define a color with the possibility to use colors built into the system (called "system colors" in SWT).
+ * <p>
+ * A {@link ColorDefinition} is defined either by a {@link BuiltInColor} constant, in which case <code>r==g==b==-1</code>, or by red / green
+ * / blue components, in which case <code>builtInColor==null</code>
+ */
+public class ColorDefinition
+{
+ /**
+ * The built-in color constant defining this color.
+ */
+ public final ColorDefinition.BuiltInColor builtInColor;
+ /**
+ * The red color component defining this color.
+ */
+ public final int r;
+ /**
+ * The green color component defining this color.
+ */
+ public final int g;
+ /**
+ * The blue color component defining this color.
+ */
+ public final int b;
+
+ public ColorDefinition(ColorDefinition.BuiltInColor col)
+ {
+ if (col == null)
+ throw new IllegalArgumentException("Illegal built-in color: " + col);
+ this.builtInColor = col;
+ this.r = -1;
+ this.g = -1;
+ this.b = -1;
+ }
+
+ public ColorDefinition(int r, int g, int b)
+ {
+ if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255)
+ throw new IllegalArgumentException("Illegal color components: r=" + r + "; g=" + g + "; b=" + b);
+ this.builtInColor = null;
+ this.r = r;
+ this.g = g;
+ this.b = b;
+ }
+
+ public static enum BuiltInColor
+ {
+ COLOR_WHITE, COLOR_BLACK, COLOR_RED, COLOR_DARK_RED, COLOR_GREEN, COLOR_DARK_GREEN, COLOR_YELLOW, COLOR_DARK_YELLOW, COLOR_BLUE,
+ COLOR_DARK_BLUE, COLOR_MAGENTA, COLOR_DARK_MAGENTA, COLOR_CYAN, COLOR_DARK_CYAN, COLOR_GRAY, COLOR_DARK_GRAY;
+ }
+
+}
\ No newline at end of file
--- /dev/null
+package mograsim.logic.core.types;
+
+/**
+ * Interface for types that support the basic logic operations
+ *
+ * @author Christian Femers
+ *
+ * @param <T> the logic type itself, to make the operations closed in T
+ * @param <S> the operand type, may be the same as T, see {@link StrictLogicType}
+ */
+public interface LogicType<T extends LogicType<T, S>, S>
+{
+ /**
+ * Determines the result when two signals meet each other directly, also called resolution (IEEE 1164)
+ *
+ * For example:
+ *
+ * <pre>
+ * 0 joined 0 == 0
+ * 1 joined 0 == X
+ * 0 joined 1 == X
+ * 1 joined 1 == 1
+ * </pre>
+ *
+ * @param t the second logic signal
+ * @return the resulting value
+ * @author Christian Femers
+ */
+ T join(S t);
+
+ /**
+ * Classical logic AND
+ *
+ * For example:
+ *
+ * <pre>
+ * 0 AND 0 == 0
+ * 1 AND 0 == 0
+ * 0 AND 1 == 0
+ * 1 AND 1 == 1
+ * </pre>
+ *
+ * @param t the second logic signal
+ * @return the resulting value
+ * @author Christian Femers
+ */
+ T and(S t);
+
+ /**
+ * Classical logic OR
+ *
+ * For example:
+ *
+ * <pre>
+ * 0 OR 0 == 0
+ * 1 OR 0 == 1
+ * 0 OR 1 == 1
+ * 1 OR 1 == 1
+ * </pre>
+ *
+ * @param t the second logic signal
+ * @return the resulting value
+ * @author Christian Femers
+ */
+ T or(S t);
+
+ /**
+ * Classical logic XOR (exclusive OR)
+ *
+ * For example:
+ *
+ * <pre>
+ * 0 XOR 0 == 0
+ * 1 XOR 0 == 1
+ * 0 XOR 1 == 1
+ * 1 XOR 1 == 0
+ * </pre>
+ *
+ * @param t the second logic signal
+ * @return the resulting value
+ * @author Christian Femers
+ */
+ T xor(S t);
+
+ /**
+ * Classical logic NOT (logical negation)
+ *
+ * For example:
+ *
+ * <pre>
+ * NOT 0 == 1
+ * NOT 1 == 0
+ * </pre>
+ *
+ * @return the resulting value
+ * @author Christian Femers
+ */
+ T not();
+
+ /**
+ * {@link #and(Object) AND} and then {@link #not() NOT}
+ *
+ * @author Christian Femers
+ */
+ default T nand(S t)
+ {
+ return and(t).not();
+ }
+
+ /**
+ * {@link #or(Object) OR} and then {@link #not() NOT}
+ *
+ * @author Christian Femers
+ */
+ default T nor(S t)
+ {
+ return or(t).not();
+ }
+
+ /**
+ * {@link #xor(Object) XOR} and then {@link #not() NOT}<br>
+ * Used to determine equality (alias parity, consistency)
+ *
+ * @author Christian Femers
+ */
+ default T xnor(S t)
+ {
+ return xor(t).not();
+ }
+}
--- /dev/null
+package mograsim.logic.core.types;
+
+import java.util.function.BiFunction;
+
+import mograsim.logic.core.types.BitVector.BitVectorMutator;
+
+@FunctionalInterface
+public interface MutationOperation extends BiFunction<BitVectorMutator, BitVector, BitVectorMutator>
+{
+ // no changes necessary, only for convenience and readability
+}
--- /dev/null
+package mograsim.logic.core.types;
+
+/**
+ * Interface for types that support the basic logic operations only among themselves, making it mathematically closed
+ *
+ * @author Christian Femers
+ * @see LogicType
+ *
+ * @param <T> the logic type itself to make the operations closed
+ */
+public interface StrictLogicType<T extends StrictLogicType<T>> extends LogicType<T, T>
+{
+ // this is just a matter of type parameters
+}
--- /dev/null
+package mograsim.logic.core.wires;
+
+import static mograsim.logic.core.types.Bit.U;
+import static mograsim.logic.core.types.Bit.Z;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import mograsim.logic.core.timeline.Timeline;
+import mograsim.logic.core.types.Bit;
+import mograsim.logic.core.types.BitVector;
+import mograsim.logic.core.types.BitVector.BitVectorMutator;
+
+/**
+ * Represents an array of wires that can store n bits of information.
+ *
+ * @author Fabian Stemmler
+ *
+ */
+public class Wire
+{
+ private BitVector values;
+ public final int travelTime;
+ private List<ReadEnd> attached = new ArrayList<ReadEnd>();
+ public final int length;
+ private List<ReadWriteEnd> inputs = new ArrayList<ReadWriteEnd>();
+ private Timeline timeline;
+
+ public Wire(Timeline timeline, int length, int travelTime)
+ {
+ if (length < 1)
+ throw new IllegalArgumentException(
+ String.format("Tried to create an array of wires with length %d, but a length of less than 1 makes no sense.", length));
+ this.timeline = timeline;
+ this.length = length;
+ this.travelTime = travelTime;
+ initValues();
+ }
+
+ private void initValues()
+ {
+ values = U.toVector(length);
+ }
+
+ private void recalculateSingleInput()
+ {
+ setNewValues(inputs.get(0).getInputValues());
+ }
+
+ private void recalculateMultipleInputs()
+ {
+ BitVectorMutator mutator = BitVectorMutator.empty();
+ for (ReadWriteEnd wireArrayEnd : inputs)
+ mutator.join(wireArrayEnd.getInputValues());
+ setNewValues(mutator.get());
+ }
+
+ private void setNewValues(BitVector newValues)
+ {
+ if (values.equals(newValues))
+ return;
+ BitVector oldValues = values;
+ values = newValues;
+ notifyObservers(oldValues);
+ }
+
+ private void recalculate()
+ {
+ switch (inputs.size())
+ {
+ case 0:
+ return;
+ case 1:
+ recalculateSingleInput();
+ break;
+ default:
+ recalculateMultipleInputs();
+ }
+ }
+
+ /**
+ * The {@link Wire} is interpreted as an unsigned integer with n bits.
+ *
+ * @return <code>true</code> if all bits are either <code>Bit.ONE</code> or <code>Bit.ZERO</code> (they do not all have to have the same
+ * value), not <code>Bit.X</code> or <code>Bit.Z</code>. <code>false</code> is returned otherwise.
+ *
+ * @author Fabian Stemmler
+ */
+ public boolean hasNumericValue()
+ {
+ for (Bit b : values)
+ {
+ if (b != Bit.ZERO && b != Bit.ONE)
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * The {@link Wire} is interpreted as an unsigned integer with n bits.
+ *
+ * @return The unsigned value of the {@link Wire}'s bits, where value 0 corresponds with 2^0, value 1 is 2^1 and so on.
+ *
+ * @author Fabian Stemmler
+ */
+ public long getUnsignedValue()
+ {
+ long val = 0;
+ long mask = 1;
+ for (Bit bit : values)
+ {
+ switch (bit)
+ {
+ default:
+ case Z:
+ case X:
+ return 0; // TODO: Proper handling for getUnsignedValue(), if not all bits are 1 or 0;
+ case ONE:
+ val |= mask;
+ break;
+ case ZERO:
+ }
+ mask = mask << 1;
+ }
+ return val;
+ }
+
+ /**
+ * The {@link Wire} is interpreted as a signed integer with n bits.
+ *
+ * @return The signed value of the {@link Wire}'s bits, where value 0 corresponds with 2^0, value 1 is 2^1 and so on.
+ *
+ * @author Fabian Stemmler
+ */
+ public long getSignedValue()
+ {
+ long val = getUnsignedValue();
+ long mask = 1 << (length - 1);
+ if ((mask & val) != 0)
+ {
+ int shifts = 64 - length;
+ return (val << shifts) >> shifts;
+ }
+ return val;
+ }
+
+ public Bit getValue()
+ {
+ return getValue(0);
+ }
+
+ public Bit getValue(int index)
+ {
+ return values.getBit(index);
+ }
+
+ public BitVector getValues(int start, int end)
+ {
+ return values.subVector(start, end);
+ }
+
+ public BitVector getValues()
+ {
+ return values;
+ }
+
+ /**
+ * Adds an {@link WireObserver}, who will be notified when the value of the {@link Wire} is updated.
+ *
+ * @param ob The {@link WireObserver} to be notified of changes.
+ * @return true if the given {@link WireObserver} was not already registered, false otherwise
+ *
+ * @author Fabian Stemmler
+ */
+ private void attachEnd(ReadEnd end)
+ {
+ attached.add(end);
+ }
+
+ private void detachEnd(ReadEnd end)
+ {
+ attached.remove(end);
+ }
+
+ private void notifyObservers(BitVector oldValues)
+ {
+ for (ReadEnd o : attached)
+ o.update(oldValues);
+ }
+
+ /**
+ * Create and register a {@link ReadWriteEnd} object, which is tied to this {@link Wire}. This {@link ReadWriteEnd} can be written to.
+ */
+ public ReadWriteEnd createReadWriteEnd()
+ {
+ return new ReadWriteEnd();
+ }
+
+ /**
+ * Create a {@link ReadEnd} object, which is tied to this {@link Wire}. This {@link ReadEnd} cannot be written to.
+ */
+ public ReadEnd createReadOnlyEnd()
+ {
+ return new ReadEnd();
+ }
+
+ private void registerInput(ReadWriteEnd toRegister)
+ {
+ inputs.add(toRegister);
+ }
+
+ /**
+ * A {@link ReadEnd} feeds a constant signal into the {@link Wire} it is tied to. The combination of all inputs determines the
+ * {@link Wire}s final value. X dominates all other inputs Z does not affect the final value, unless there are no other inputs than Z 0
+ * and 1 turn into X when they are mixed
+ *
+ * @author Fabian Stemmler
+ */
+ public class ReadEnd
+ {
+ private List<WireObserver> observers = new ArrayList<WireObserver>();
+
+ private ReadEnd()
+ {
+ super();
+ Wire.this.attachEnd(this);
+ }
+
+ public void update(BitVector oldValues)
+ {
+ for (WireObserver ob : observers)
+ ob.update(this, oldValues);
+ }
+
+ /**
+ * Included for convenient use on {@link Wire}s of length 1.
+ *
+ * @return The value of bit 0.
+ *
+ * @author Fabian Stemmler
+ */
+ public Bit getValue()
+ {
+ return Wire.this.getValue();
+ }
+
+ /**
+ * @param index Index of the requested bit.
+ * @return The value of the indexed bit.
+ *
+ * @author Fabian Stemmler
+ */
+ public Bit getValue(int index)
+ {
+ return Wire.this.getValue(index);
+ }
+
+ /**
+ * @param index Index of the requested bit.
+ * @return The value of the indexed bit.
+ *
+ * @author Fabian Stemmler
+ */
+ public BitVector getValues()
+ {
+ return Wire.this.getValues();
+ }
+
+ /**
+ * @param start Start of the wanted segment. (inclusive)
+ * @param end End of the wanted segment. (exclusive)
+ * @return The values of the segment of {@link Bit}s indexed.
+ *
+ * @author Fabian Stemmler
+ */
+ public BitVector getValues(int start, int end)
+ {
+ return Wire.this.getValues(start, end);
+ }
+
+ /**
+ * The {@link Wire} is interpreted as an unsigned integer with n bits.
+ *
+ * @return <code>true</code> if all bits are either <code>Bit.ONE</code> or <code>Bit.ZERO</code> (they do not all have to have the
+ * same value), not <code>Bit.X</code> or <code>Bit.Z</code>. <code>false</code> is returned otherwise.
+ *
+ * @author Fabian Stemmler
+ */
+ public boolean hasNumericValue()
+ {
+ return Wire.this.hasNumericValue();
+ }
+
+ /**
+ * The {@link Wire} is interpreted as an unsigned integer with n bits.
+ *
+ * @return The unsigned value of the {@link Wire}'s bits, where value 0 corresponds with 2^0, value 1 is 2^1 and so on.
+ *
+ * @author Fabian Stemmler
+ */
+ public long getUnsignedValue()
+ {
+ return Wire.this.getUnsignedValue();
+ }
+
+ /**
+ * The {@link Wire} is interpreted as a signed integer with n bits.
+ *
+ * @return The signed value of the {@link Wire}'s bits, where value 0 corresponds with 2^0, value 1 is 2^1 and so on.
+ *
+ * @author Fabian Stemmler
+ */
+ public long getSignedValue()
+ {
+ return Wire.this.getSignedValue();
+ }
+
+ @Override
+ public String toString()
+ {
+ return Wire.this.toString();
+ }
+
+ public void close()
+ {
+ inputs.remove(this);
+ detachEnd(this);
+ recalculate();
+ }
+
+ public int length()
+ {
+ return length;
+ }
+
+ public boolean addObserver(WireObserver ob)
+ {
+ return observers.add(ob);
+ }
+
+ public Wire getWire()
+ {
+ return Wire.this;
+ }
+ }
+
+ public class ReadWriteEnd extends ReadEnd
+ {
+ private boolean open;
+ private BitVector inputValues;
+
+ private ReadWriteEnd()
+ {
+ super();
+ open = true;
+ initValues();
+ registerInput(this);
+ }
+
+ private void initValues()
+ {
+ inputValues = U.toVector(length);
+ }
+
+ /**
+ * Sets the wires values. This takes up time, as specified by the {@link Wire}s travel time.
+ *
+ * @param newValues The new values the wires should take on.
+ *
+ * @author Fabian Stemmler
+ */
+ public void feedSignals(Bit... newValues)
+ {
+ feedSignals(BitVector.of(newValues));
+ }
+
+ public void feedSignals(BitVector newValues)
+ {
+ if (newValues.length() != length)
+ throw new IllegalArgumentException(
+ String.format("Attempted to input %d bits instead of %d bits.", newValues.length(), length));
+ if (!open)
+ throw new RuntimeException("Attempted to write to closed WireArrayEnd.");
+ timeline.addEvent(e -> setValues(newValues), travelTime);
+ }
+
+ /**
+ * Sets values of a subarray of wires. This takes up time, as specified by the {@link Wire}s travel time.
+ *
+ * @param bitVector The new values the wires should take on.
+ * @param startingBit The first index of the subarray of wires.
+ *
+ * @author Fabian Stemmler
+ */
+ public void feedSignals(int startingBit, BitVector bitVector)
+ {
+ if (!open)
+ throw new RuntimeException("Attempted to write to closed WireArrayEnd.");
+ timeline.addEvent(e -> setValues(startingBit, bitVector), travelTime);
+ }
+
+ private void setValues(int startingBit, BitVector newValues)
+ {
+ // index check covered in equals
+ if (!inputValues.equalsWithOffset(newValues, startingBit))
+ {
+ Bit[] vals = inputValues.getBits();
+ System.arraycopy(newValues.getBits(), 0, vals, startingBit, newValues.length());
+ inputValues = BitVector.of(vals);
+ Wire.this.recalculate();
+ }
+ }
+
+ private void setValues(BitVector newValues)
+ {
+ if (inputValues.equals(newValues))
+ return;
+ inputValues = newValues;
+ Wire.this.recalculate();
+ }
+
+ /**
+ * @return The value (of bit 0) the {@link ReadEnd} is currently feeding into the associated {@link Wire}.
+ */
+ public Bit getInputValue()
+ {
+ return getInputValue(0);
+ }
+
+ /**
+ * @return The value which the {@link ReadEnd} is currently feeding into the associated {@link Wire} at the indexed {@link Bit}.
+ */
+ public Bit getInputValue(int index)
+ {
+ return inputValues.getBit(index);
+ }
+
+ /**
+ * @return A copy (safe to modify) of the values the {@link ReadEnd} is currently feeding into the associated {@link Wire}.
+ */
+ public BitVector getInputValues()
+ {
+ return getInputValues(0, length);
+ }
+
+ public BitVector getInputValues(int start, int end)
+ {
+ return inputValues.subVector(start, end);
+ }
+
+ /**
+ * {@link ReadEnd} now feeds Z into the associated {@link Wire}.
+ */
+ public void clearSignals()
+ {
+ feedSignals(Z.toVector(length));
+ }
+
+ public BitVector wireValuesExcludingMe()
+ {
+ BitVectorMutator mutator = BitVectorMutator.empty();
+ for (ReadWriteEnd wireEnd : inputs)
+ {
+ if (wireEnd == this)
+ continue;
+ mutator.join(wireEnd.inputValues);
+ }
+ return mutator.get();
+ }
+
+ @Override
+ public String toString()
+ {
+ return inputValues.toString();
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("wire 0x%08x value: %s inputs: %s", hashCode(), values, inputs);
+ // Arrays.toString(values), inputs.stream().map(i -> Arrays.toString(i.inputValues)).reduce((s1, s2) -> s1 + s2)
+ }
+
+ public static ReadEnd[] extractEnds(Wire[] w)
+ {
+ ReadEnd[] inputs = new ReadEnd[w.length];
+ for (int i = 0; i < w.length; i++)
+ inputs[i] = w[i].createReadWriteEnd();
+ return inputs;
+ }
+}
\ No newline at end of file
--- /dev/null
+package mograsim.logic.core.wires;
+
+import mograsim.logic.core.types.BitVector;
+import mograsim.logic.core.wires.Wire.ReadEnd;
+
+public interface WireObserver
+{
+ public void update(ReadEnd initiator, BitVector oldValues);
+}