From: Daniel Kirschten Date: Wed, 29 May 2019 20:59:35 +0000 (+0200) Subject: Renamed project to MoGraSim X-Git-Url: https://mograsim.net/gitweb/?a=commitdiff_plain;h=a28f7aa0dab4248e99159c5a647676170cb17a4e;p=Mograsim.git Renamed project to MoGraSim --- diff --git a/LogicUI/.project b/LogicUI/.project index cc7eae3e..9ba08bbd 100644 --- a/LogicUI/.project +++ b/LogicUI/.project @@ -1,9 +1,9 @@ - LogicUI + mograsim.logic.ui - era.mi + mograsim.logic.core SWTZoomableCanvas diff --git a/LogicUI/.settings/org.eclipse.jdt.ui.prefs b/LogicUI/.settings/org.eclipse.jdt.ui.prefs index 715c6cc2..8f2c0a47 100644 --- a/LogicUI/.settings/org.eclipse.jdt.ui.prefs +++ b/LogicUI/.settings/org.eclipse.jdt.ui.prefs @@ -1,6 +1,6 @@ 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 diff --git a/LogicUI/META-INF/MANIFEST.MF b/LogicUI/META-INF/MANIFEST.MF index 9f8199a3..c1dcd55a 100644 --- a/LogicUI/META-INF/MANIFEST.MF +++ b/LogicUI/META-INF/MANIFEST.MF @@ -1,17 +1,17 @@ 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 diff --git a/LogicUI/src/era/mi/gui/ColorHelper.java b/LogicUI/src/era/mi/gui/ColorHelper.java deleted file mode 100644 index ff6a4950..00000000 --- a/LogicUI/src/era/mi/gui/ColorHelper.java +++ /dev/null @@ -1,91 +0,0 @@ -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 getColor, Consumer 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 diff --git a/LogicUI/src/era/mi/gui/LogicExecuter.java b/LogicUI/src/era/mi/gui/LogicExecuter.java deleted file mode 100644 index 65abd4c9..00000000 --- a/LogicUI/src/era/mi/gui/LogicExecuter.java +++ /dev/null @@ -1,105 +0,0 @@ -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 diff --git a/LogicUI/src/era/mi/gui/LogicUICanvas.java b/LogicUI/src/era/mi/gui/LogicUICanvas.java deleted file mode 100644 index 63550443..00000000 --- a/LogicUI/src/era/mi/gui/LogicUICanvas.java +++ /dev/null @@ -1,114 +0,0 @@ -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 redrawConsumer = o -> redrawThreadsafe(); - Consumer pinAddedListener = p -> - { - p.addPinMovedListener(redrawConsumer); - redrawThreadsafe(); - }; - Consumer pinRemovedListener = p -> - { - p.removePinMovedListener(redrawConsumer); - redrawThreadsafe(); - }; - Consumer 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 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 diff --git a/LogicUI/src/era/mi/gui/LogicUIStandaloneGUI.java b/LogicUI/src/era/mi/gui/LogicUIStandaloneGUI.java deleted file mode 100644 index 55931cfb..00000000 --- a/LogicUI/src/era/mi/gui/LogicUIStandaloneGUI.java +++ /dev/null @@ -1,59 +0,0 @@ -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 diff --git a/LogicUI/src/era/mi/gui/SimpleLogicUIStandalone.java b/LogicUI/src/era/mi/gui/SimpleLogicUIStandalone.java deleted file mode 100644 index 0573aa90..00000000 --- a/LogicUI/src/era/mi/gui/SimpleLogicUIStandalone.java +++ /dev/null @@ -1,38 +0,0 @@ -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 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 diff --git a/LogicUI/src/era/mi/gui/examples/RSLatchExample.java b/LogicUI/src/era/mi/gui/examples/RSLatchExample.java deleted file mode 100644 index 08c42c5d..00000000 --- a/LogicUI/src/era/mi/gui/examples/RSLatchExample.java +++ /dev/null @@ -1,61 +0,0 @@ -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 diff --git a/LogicUI/src/era/mi/gui/model/ViewModel.java b/LogicUI/src/era/mi/gui/model/ViewModel.java deleted file mode 100644 index 7eb55c18..00000000 --- a/LogicUI/src/era/mi/gui/model/ViewModel.java +++ /dev/null @@ -1,110 +0,0 @@ -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 components; - private final List componentsUnmodifiable; - private final List wires; - private final List wiresUnmodifiable; - - private final List> componentAddedListeners; - private final List> componentRemovedListeners; - private final List> wireAddedListeners; - private final List> 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 getComponents() - { - return componentsUnmodifiable; - } - - public List getWires() - { - return wiresUnmodifiable; - } - - // @formatter:off - public void addComponentAddedListener (Consumer listener){componentAddedListeners .add (listener);} - public void addComponentRemovedListener (Consumer listener){componentRemovedListeners.add (listener);} - public void addWireAddedListener (Consumer listener){wireAddedListeners .add (listener);} - public void addWireRemovedListener (Consumer listener){wireRemovedListeners .add (listener);} - - public void removeComponentAddedListener (Consumer listener){componentAddedListeners .remove(listener);} - public void removeComponentRemovedListener(Consumer listener){componentRemovedListeners.remove(listener);} - public void removeWireAddedListener (Consumer listener){wireAddedListeners .remove(listener);} - public void removeWireRemovedListener (Consumer 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 diff --git a/LogicUI/src/era/mi/gui/model/components/GUIAndGate.java b/LogicUI/src/era/mi/gui/model/components/GUIAndGate.java deleted file mode 100644 index 65f1db4e..00000000 --- a/LogicUI/src/era/mi/gui/model/components/GUIAndGate.java +++ /dev/null @@ -1,12 +0,0 @@ -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 diff --git a/LogicUI/src/era/mi/gui/model/components/GUIComponent.java b/LogicUI/src/era/mi/gui/model/components/GUIComponent.java deleted file mode 100644 index b8f49f34..00000000 --- a/LogicUI/src/era/mi/gui/model/components/GUIComponent.java +++ /dev/null @@ -1,119 +0,0 @@ -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 pins; - protected final List pinsUnmodifiable; - - private final List> componentLookChangedListeners; - private final List> componentMovedListeners; - private final List> pinAddedListeners; - private final List> 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 getPins() - { - return pinsUnmodifiable; - } - - // @formatter:off - public void addComponentLookChangedListener (Consumer listener) {componentLookChangedListeners.add (listener);} - public void addComponentMovedListener (Consumer listener) {componentMovedListeners .add (listener);} - public void addPinAddedListener (Consumer listener) {pinAddedListeners .add (listener);} - public void addPinRemovedListener (Consumer listener) {pinRemovedListeners .add (listener);} - - public void removeComponentLookChangedListener(Consumer listener) {componentLookChangedListeners.remove(listener);} - public void removeComponentMovedListener (Consumer listener) {componentMovedListeners .remove(listener);} - public void removePinAddedListener (Consumer listener) {pinAddedListeners .remove(listener);} - public void removePinRemovedListener (Consumer 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 diff --git a/LogicUI/src/era/mi/gui/model/components/GUIManualSwitch.java b/LogicUI/src/era/mi/gui/model/components/GUIManualSwitch.java deleted file mode 100644 index b2a8b018..00000000 --- a/LogicUI/src/era/mi/gui/model/components/GUIManualSwitch.java +++ /dev/null @@ -1,67 +0,0 @@ -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 diff --git a/LogicUI/src/era/mi/gui/model/components/GUINotGate.java b/LogicUI/src/era/mi/gui/model/components/GUINotGate.java deleted file mode 100644 index c22d9b28..00000000 --- a/LogicUI/src/era/mi/gui/model/components/GUINotGate.java +++ /dev/null @@ -1,12 +0,0 @@ -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 diff --git a/LogicUI/src/era/mi/gui/model/components/GUIOrGate.java b/LogicUI/src/era/mi/gui/model/components/GUIOrGate.java deleted file mode 100644 index a732509b..00000000 --- a/LogicUI/src/era/mi/gui/model/components/GUIOrGate.java +++ /dev/null @@ -1,12 +0,0 @@ -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 diff --git a/LogicUI/src/era/mi/gui/model/components/SimpleRectangularGUIGate.java b/LogicUI/src/era/mi/gui/model/components/SimpleRectangularGUIGate.java deleted file mode 100644 index eb396cca..00000000 --- a/LogicUI/src/era/mi/gui/model/components/SimpleRectangularGUIGate.java +++ /dev/null @@ -1,89 +0,0 @@ -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 inputPins; - private final List 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 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 diff --git a/LogicUI/src/era/mi/gui/model/wires/GUIWire.java b/LogicUI/src/era/mi/gui/model/wires/GUIWire.java deleted file mode 100644 index 6bcbd1c5..00000000 --- a/LogicUI/src/era/mi/gui/model/wires/GUIWire.java +++ /dev/null @@ -1,100 +0,0 @@ -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> 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 listener) {wireLookChangedListeners.add (listener);} - - public void removeWireLookChangedListener(Consumer listener) {wireLookChangedListeners.remove(listener);} - - private void callWireLookChangedListeners() {wireLookChangedListeners.forEach(l -> l.accept(this));} - // @formatter:on - -} \ No newline at end of file diff --git a/LogicUI/src/era/mi/gui/model/wires/MovablePin.java b/LogicUI/src/era/mi/gui/model/wires/MovablePin.java deleted file mode 100644 index 949e7d76..00000000 --- a/LogicUI/src/era/mi/gui/model/wires/MovablePin.java +++ /dev/null @@ -1,17 +0,0 @@ -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 diff --git a/LogicUI/src/era/mi/gui/model/wires/Pin.java b/LogicUI/src/era/mi/gui/model/wires/Pin.java deleted file mode 100644 index abafc071..00000000 --- a/LogicUI/src/era/mi/gui/model/wires/Pin.java +++ /dev/null @@ -1,68 +0,0 @@ -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> 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 listener){pinMovedListeners.add (listener);} - - public void removePinMovedListener(Consumer 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 diff --git a/LogicUI/src/era/mi/gui/model/wires/WireCrossPoint.java b/LogicUI/src/era/mi/gui/model/wires/WireCrossPoint.java deleted file mode 100644 index 04c5f1e8..00000000 --- a/LogicUI/src/era/mi/gui/model/wires/WireCrossPoint.java +++ /dev/null @@ -1,49 +0,0 @@ -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 diff --git a/LogicUI/src/era/mi/gui/modeladapter/LogicModelParameters.java b/LogicUI/src/era/mi/gui/modeladapter/LogicModelParameters.java deleted file mode 100644 index 435f7086..00000000 --- a/LogicUI/src/era/mi/gui/modeladapter/LogicModelParameters.java +++ /dev/null @@ -1,7 +0,0 @@ -package era.mi.gui.modeladapter; - -public class LogicModelParameters -{ - public int wireTravelTime; - public int gateProcessTime; -} \ No newline at end of file diff --git a/LogicUI/src/era/mi/gui/modeladapter/ViewLogicModelAdapter.java b/LogicUI/src/era/mi/gui/modeladapter/ViewLogicModelAdapter.java deleted file mode 100644 index 1e7d341e..00000000 --- a/LogicUI/src/era/mi/gui/modeladapter/ViewLogicModelAdapter.java +++ /dev/null @@ -1,146 +0,0 @@ -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, ComponentAdapter> componentAdapters; - static - { - Set> 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 logicWiresPerPin = convertWires( - viewModel.getComponents().stream().flatMap(component -> component.getPins().stream()).collect(Collectors.toSet()), - viewModel.getWires(), params, timeline); - Map logicWiresPerPinUnmodifiable = Collections.unmodifiableMap(logicWiresPerPin); - - Map 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 logicComponents = new ArrayList<>(); - logicComponents.addAll(oneToOneComponents.values()); - - return timeline; - } - - private static Map convertWires(Set allPins, List wires, LogicModelParameters params, Timeline timeline) - { - Map> connectedPinGroups = getConnectedPinGroups(allPins, wires); - Map logicWiresPerPin = createLogicWires(params, timeline, connectedPinGroups); - setGUIWiresLogicModelBinding(wires, logicWiresPerPin); - return logicWiresPerPin; - } - - private static Map createLogicWires(LogicModelParameters params, Timeline timeline, Map> connectedPinGroups) - { - Map logicWiresPerPin = new HashMap<>(); - Map, Wire> logicWiresPerPinGroup = new HashMap<>(); - for (Entry> 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 wires, Map logicWiresPerPin) - { - Map 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> getConnectedPinGroups(Set allPins, List wires) - { - Map> connectedPinsPerPin = new HashMap<>(); - - for (Pin p : allPins) - { - HashSet connectedPins = new HashSet<>(); - connectedPins.add(p); - connectedPinsPerPin.put(p, connectedPins); - } - - wires.forEach(wire -> - { - Pin pin1 = wire.getPin1(); - Pin pin2 = wire.getPin2(); - - Set pin1ConnectedPins = connectedPinsPerPin.get(pin1); - Set 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 Component createAndLinkComponent(Timeline timeline, LogicModelParameters params, - GUIComponent guiComponent, Map logicWiresPerPin, ComponentAdapter 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 diff --git a/LogicUI/src/era/mi/gui/modeladapter/componentadapters/ComponentAdapter.java b/LogicUI/src/era/mi/gui/modeladapter/componentadapters/ComponentAdapter.java deleted file mode 100644 index c109d732..00000000 --- a/LogicUI/src/era/mi/gui/modeladapter/componentadapters/ComponentAdapter.java +++ /dev/null @@ -1,18 +0,0 @@ -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 -{ - public Class getSupportedClass(); - - public Component createAndLinkComponent(Timeline timeline, LogicModelParameters params, G guiComponent, - Map logicWiresPerPin); -} \ No newline at end of file diff --git a/LogicUI/src/era/mi/gui/modeladapter/componentadapters/ManualSwitchAdapter.java b/LogicUI/src/era/mi/gui/modeladapter/componentadapters/ManualSwitchAdapter.java deleted file mode 100644 index 3a6ea050..00000000 --- a/LogicUI/src/era/mi/gui/modeladapter/componentadapters/ManualSwitchAdapter.java +++ /dev/null @@ -1,31 +0,0 @@ -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 -{ - @Override - public Class getSupportedClass() - { - return GUIManualSwitch.class; - } - - @Override - public Component createAndLinkComponent(Timeline timeline, LogicModelParameters params, GUIManualSwitch guiComponent, - Map 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 diff --git a/LogicUI/src/era/mi/gui/modeladapter/componentadapters/SimpleGateAdapter.java b/LogicUI/src/era/mi/gui/modeladapter/componentadapters/SimpleGateAdapter.java deleted file mode 100644 index b244973b..00000000 --- a/LogicUI/src/era/mi/gui/modeladapter/componentadapters/SimpleGateAdapter.java +++ /dev/null @@ -1,48 +0,0 @@ -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 implements ComponentAdapter -{ - private final Class supportedClass; - private final ComponentConstructor constructor; - - public SimpleGateAdapter(Class supportedClass, ComponentConstructor constructor) - { - this.supportedClass = supportedClass; - this.constructor = constructor; - } - - @Override - public Class getSupportedClass() - { - return supportedClass; - } - - @Override - public Component createAndLinkComponent(Timeline timeline, LogicModelParameters params, G guiComponent, Map logicWiresPerPin) - { - ReadWriteEnd out = logicWiresPerPin.get(guiComponent.getOutputPin()).createReadWriteEnd(); - List 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 diff --git a/LogicUI/src/mograsim/logic/ui/ColorHelper.java b/LogicUI/src/mograsim/logic/ui/ColorHelper.java new file mode 100644 index 00000000..886c6f76 --- /dev/null +++ b/LogicUI/src/mograsim/logic/ui/ColorHelper.java @@ -0,0 +1,91 @@ +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 getColor, Consumer 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 diff --git a/LogicUI/src/mograsim/logic/ui/LogicExecuter.java b/LogicUI/src/mograsim/logic/ui/LogicExecuter.java new file mode 100644 index 00000000..dec5c53a --- /dev/null +++ b/LogicUI/src/mograsim/logic/ui/LogicExecuter.java @@ -0,0 +1,105 @@ +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 diff --git a/LogicUI/src/mograsim/logic/ui/LogicUICanvas.java b/LogicUI/src/mograsim/logic/ui/LogicUICanvas.java new file mode 100644 index 00000000..4761839b --- /dev/null +++ b/LogicUI/src/mograsim/logic/ui/LogicUICanvas.java @@ -0,0 +1,114 @@ +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 redrawConsumer = o -> redrawThreadsafe(); + Consumer pinAddedListener = p -> + { + p.addPinMovedListener(redrawConsumer); + redrawThreadsafe(); + }; + Consumer pinRemovedListener = p -> + { + p.removePinMovedListener(redrawConsumer); + redrawThreadsafe(); + }; + Consumer 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 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 diff --git a/LogicUI/src/mograsim/logic/ui/LogicUIStandaloneGUI.java b/LogicUI/src/mograsim/logic/ui/LogicUIStandaloneGUI.java new file mode 100644 index 00000000..f39727c8 --- /dev/null +++ b/LogicUI/src/mograsim/logic/ui/LogicUIStandaloneGUI.java @@ -0,0 +1,59 @@ +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 diff --git a/LogicUI/src/mograsim/logic/ui/SimpleLogicUIStandalone.java b/LogicUI/src/mograsim/logic/ui/SimpleLogicUIStandalone.java new file mode 100644 index 00000000..ef0beb32 --- /dev/null +++ b/LogicUI/src/mograsim/logic/ui/SimpleLogicUIStandalone.java @@ -0,0 +1,38 @@ +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 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 diff --git a/LogicUI/src/mograsim/logic/ui/examples/RSLatchExample.java b/LogicUI/src/mograsim/logic/ui/examples/RSLatchExample.java new file mode 100644 index 00000000..f787c815 --- /dev/null +++ b/LogicUI/src/mograsim/logic/ui/examples/RSLatchExample.java @@ -0,0 +1,61 @@ +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 diff --git a/LogicUI/src/mograsim/logic/ui/model/ViewModel.java b/LogicUI/src/mograsim/logic/ui/model/ViewModel.java new file mode 100644 index 00000000..31cd1a8e --- /dev/null +++ b/LogicUI/src/mograsim/logic/ui/model/ViewModel.java @@ -0,0 +1,110 @@ +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 components; + private final List componentsUnmodifiable; + private final List wires; + private final List wiresUnmodifiable; + + private final List> componentAddedListeners; + private final List> componentRemovedListeners; + private final List> wireAddedListeners; + private final List> 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 getComponents() + { + return componentsUnmodifiable; + } + + public List getWires() + { + return wiresUnmodifiable; + } + + // @formatter:off + public void addComponentAddedListener (Consumer listener){componentAddedListeners .add (listener);} + public void addComponentRemovedListener (Consumer listener){componentRemovedListeners.add (listener);} + public void addWireAddedListener (Consumer listener){wireAddedListeners .add (listener);} + public void addWireRemovedListener (Consumer listener){wireRemovedListeners .add (listener);} + + public void removeComponentAddedListener (Consumer listener){componentAddedListeners .remove(listener);} + public void removeComponentRemovedListener(Consumer listener){componentRemovedListeners.remove(listener);} + public void removeWireAddedListener (Consumer listener){wireAddedListeners .remove(listener);} + public void removeWireRemovedListener (Consumer 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 diff --git a/LogicUI/src/mograsim/logic/ui/model/components/GUIAndGate.java b/LogicUI/src/mograsim/logic/ui/model/components/GUIAndGate.java new file mode 100644 index 00000000..8efea1c4 --- /dev/null +++ b/LogicUI/src/mograsim/logic/ui/model/components/GUIAndGate.java @@ -0,0 +1,12 @@ +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 diff --git a/LogicUI/src/mograsim/logic/ui/model/components/GUIComponent.java b/LogicUI/src/mograsim/logic/ui/model/components/GUIComponent.java new file mode 100644 index 00000000..c3e4529f --- /dev/null +++ b/LogicUI/src/mograsim/logic/ui/model/components/GUIComponent.java @@ -0,0 +1,119 @@ +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 pins; + protected final List pinsUnmodifiable; + + private final List> componentLookChangedListeners; + private final List> componentMovedListeners; + private final List> pinAddedListeners; + private final List> 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 getPins() + { + return pinsUnmodifiable; + } + + // @formatter:off + public void addComponentLookChangedListener (Consumer listener) {componentLookChangedListeners.add (listener);} + public void addComponentMovedListener (Consumer listener) {componentMovedListeners .add (listener);} + public void addPinAddedListener (Consumer listener) {pinAddedListeners .add (listener);} + public void addPinRemovedListener (Consumer listener) {pinRemovedListeners .add (listener);} + + public void removeComponentLookChangedListener(Consumer listener) {componentLookChangedListeners.remove(listener);} + public void removeComponentMovedListener (Consumer listener) {componentMovedListeners .remove(listener);} + public void removePinAddedListener (Consumer listener) {pinAddedListeners .remove(listener);} + public void removePinRemovedListener (Consumer 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 diff --git a/LogicUI/src/mograsim/logic/ui/model/components/GUIManualSwitch.java b/LogicUI/src/mograsim/logic/ui/model/components/GUIManualSwitch.java new file mode 100644 index 00000000..a89bc6a6 --- /dev/null +++ b/LogicUI/src/mograsim/logic/ui/model/components/GUIManualSwitch.java @@ -0,0 +1,67 @@ +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 diff --git a/LogicUI/src/mograsim/logic/ui/model/components/GUINotGate.java b/LogicUI/src/mograsim/logic/ui/model/components/GUINotGate.java new file mode 100644 index 00000000..7a6865f3 --- /dev/null +++ b/LogicUI/src/mograsim/logic/ui/model/components/GUINotGate.java @@ -0,0 +1,12 @@ +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 diff --git a/LogicUI/src/mograsim/logic/ui/model/components/GUIOrGate.java b/LogicUI/src/mograsim/logic/ui/model/components/GUIOrGate.java new file mode 100644 index 00000000..3a3c3241 --- /dev/null +++ b/LogicUI/src/mograsim/logic/ui/model/components/GUIOrGate.java @@ -0,0 +1,12 @@ +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 diff --git a/LogicUI/src/mograsim/logic/ui/model/components/SimpleRectangularGUIGate.java b/LogicUI/src/mograsim/logic/ui/model/components/SimpleRectangularGUIGate.java new file mode 100644 index 00000000..b09d9c9d --- /dev/null +++ b/LogicUI/src/mograsim/logic/ui/model/components/SimpleRectangularGUIGate.java @@ -0,0 +1,89 @@ +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 inputPins; + private final List 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 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 diff --git a/LogicUI/src/mograsim/logic/ui/model/wires/GUIWire.java b/LogicUI/src/mograsim/logic/ui/model/wires/GUIWire.java new file mode 100644 index 00000000..2512b884 --- /dev/null +++ b/LogicUI/src/mograsim/logic/ui/model/wires/GUIWire.java @@ -0,0 +1,100 @@ +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> 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 listener) {wireLookChangedListeners.add (listener);} + + public void removeWireLookChangedListener(Consumer listener) {wireLookChangedListeners.remove(listener);} + + private void callWireLookChangedListeners() {wireLookChangedListeners.forEach(l -> l.accept(this));} + // @formatter:on + +} \ No newline at end of file diff --git a/LogicUI/src/mograsim/logic/ui/model/wires/MovablePin.java b/LogicUI/src/mograsim/logic/ui/model/wires/MovablePin.java new file mode 100644 index 00000000..40364320 --- /dev/null +++ b/LogicUI/src/mograsim/logic/ui/model/wires/MovablePin.java @@ -0,0 +1,17 @@ +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 diff --git a/LogicUI/src/mograsim/logic/ui/model/wires/Pin.java b/LogicUI/src/mograsim/logic/ui/model/wires/Pin.java new file mode 100644 index 00000000..350de8d2 --- /dev/null +++ b/LogicUI/src/mograsim/logic/ui/model/wires/Pin.java @@ -0,0 +1,68 @@ +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> 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 listener){pinMovedListeners.add (listener);} + + public void removePinMovedListener(Consumer 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 diff --git a/LogicUI/src/mograsim/logic/ui/model/wires/WireCrossPoint.java b/LogicUI/src/mograsim/logic/ui/model/wires/WireCrossPoint.java new file mode 100644 index 00000000..34f69032 --- /dev/null +++ b/LogicUI/src/mograsim/logic/ui/model/wires/WireCrossPoint.java @@ -0,0 +1,49 @@ +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 diff --git a/LogicUI/src/mograsim/logic/ui/modeladapter/LogicModelParameters.java b/LogicUI/src/mograsim/logic/ui/modeladapter/LogicModelParameters.java new file mode 100644 index 00000000..010cdd1f --- /dev/null +++ b/LogicUI/src/mograsim/logic/ui/modeladapter/LogicModelParameters.java @@ -0,0 +1,7 @@ +package mograsim.logic.ui.modeladapter; + +public class LogicModelParameters +{ + public int wireTravelTime; + public int gateProcessTime; +} \ No newline at end of file diff --git a/LogicUI/src/mograsim/logic/ui/modeladapter/ViewLogicModelAdapter.java b/LogicUI/src/mograsim/logic/ui/modeladapter/ViewLogicModelAdapter.java new file mode 100644 index 00000000..076d64b6 --- /dev/null +++ b/LogicUI/src/mograsim/logic/ui/modeladapter/ViewLogicModelAdapter.java @@ -0,0 +1,146 @@ +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, ComponentAdapter> componentAdapters; + static + { + Set> 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 logicWiresPerPin = convertWires( + viewModel.getComponents().stream().flatMap(component -> component.getPins().stream()).collect(Collectors.toSet()), + viewModel.getWires(), params, timeline); + Map logicWiresPerPinUnmodifiable = Collections.unmodifiableMap(logicWiresPerPin); + + Map 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 logicComponents = new ArrayList<>(); + logicComponents.addAll(oneToOneComponents.values()); + + return timeline; + } + + private static Map convertWires(Set allPins, List wires, LogicModelParameters params, Timeline timeline) + { + Map> connectedPinGroups = getConnectedPinGroups(allPins, wires); + Map logicWiresPerPin = createLogicWires(params, timeline, connectedPinGroups); + setGUIWiresLogicModelBinding(wires, logicWiresPerPin); + return logicWiresPerPin; + } + + private static Map createLogicWires(LogicModelParameters params, Timeline timeline, Map> connectedPinGroups) + { + Map logicWiresPerPin = new HashMap<>(); + Map, Wire> logicWiresPerPinGroup = new HashMap<>(); + for (Entry> 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 wires, Map logicWiresPerPin) + { + Map 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> getConnectedPinGroups(Set allPins, List wires) + { + Map> connectedPinsPerPin = new HashMap<>(); + + for (Pin p : allPins) + { + HashSet connectedPins = new HashSet<>(); + connectedPins.add(p); + connectedPinsPerPin.put(p, connectedPins); + } + + wires.forEach(wire -> + { + Pin pin1 = wire.getPin1(); + Pin pin2 = wire.getPin2(); + + Set pin1ConnectedPins = connectedPinsPerPin.get(pin1); + Set 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 Component createAndLinkComponent(Timeline timeline, LogicModelParameters params, + GUIComponent guiComponent, Map logicWiresPerPin, ComponentAdapter 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 diff --git a/LogicUI/src/mograsim/logic/ui/modeladapter/componentadapters/ComponentAdapter.java b/LogicUI/src/mograsim/logic/ui/modeladapter/componentadapters/ComponentAdapter.java new file mode 100644 index 00000000..9be1b96e --- /dev/null +++ b/LogicUI/src/mograsim/logic/ui/modeladapter/componentadapters/ComponentAdapter.java @@ -0,0 +1,18 @@ +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 +{ + public Class getSupportedClass(); + + public Component createAndLinkComponent(Timeline timeline, LogicModelParameters params, G guiComponent, + Map logicWiresPerPin); +} \ No newline at end of file diff --git a/LogicUI/src/mograsim/logic/ui/modeladapter/componentadapters/ManualSwitchAdapter.java b/LogicUI/src/mograsim/logic/ui/modeladapter/componentadapters/ManualSwitchAdapter.java new file mode 100644 index 00000000..c27cbdf2 --- /dev/null +++ b/LogicUI/src/mograsim/logic/ui/modeladapter/componentadapters/ManualSwitchAdapter.java @@ -0,0 +1,31 @@ +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 +{ + @Override + public Class getSupportedClass() + { + return GUIManualSwitch.class; + } + + @Override + public Component createAndLinkComponent(Timeline timeline, LogicModelParameters params, GUIManualSwitch guiComponent, + Map 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 diff --git a/LogicUI/src/mograsim/logic/ui/modeladapter/componentadapters/SimpleGateAdapter.java b/LogicUI/src/mograsim/logic/ui/modeladapter/componentadapters/SimpleGateAdapter.java new file mode 100644 index 00000000..f88308bd --- /dev/null +++ b/LogicUI/src/mograsim/logic/ui/modeladapter/componentadapters/SimpleGateAdapter.java @@ -0,0 +1,48 @@ +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 implements ComponentAdapter +{ + private final Class supportedClass; + private final ComponentConstructor constructor; + + public SimpleGateAdapter(Class supportedClass, ComponentConstructor constructor) + { + this.supportedClass = supportedClass; + this.constructor = constructor; + } + + @Override + public Class getSupportedClass() + { + return supportedClass; + } + + @Override + public Component createAndLinkComponent(Timeline timeline, LogicModelParameters params, G guiComponent, Map logicWiresPerPin) + { + ReadWriteEnd out = logicWiresPerPin.get(guiComponent.getOutputPin()).createReadWriteEnd(); + List 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 diff --git a/SampleERCP/.project b/SampleERCP/.project index dd05f6c6..4577bb5e 100644 --- a/SampleERCP/.project +++ b/SampleERCP/.project @@ -1,9 +1,9 @@ - SampleERCP + mograsim.rcp - LogicUI + mograsim.rcp diff --git a/SampleERCP/.settings/org.eclipse.jdt.ui.prefs b/SampleERCP/.settings/org.eclipse.jdt.ui.prefs index 614cb32e..5ae94b62 100644 --- a/SampleERCP/.settings/org.eclipse.jdt.ui.prefs +++ b/SampleERCP/.settings/org.eclipse.jdt.ui.prefs @@ -1,6 +1,6 @@ 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 diff --git a/SampleERCP/Application.e4xmi b/SampleERCP/Application.e4xmi index 6e2ca3e3..75815684 100644 --- a/SampleERCP/Application.e4xmi +++ b/SampleERCP/Application.e4xmi @@ -1,10 +1,10 @@ - + - - + + @@ -12,8 +12,8 @@ - - + + @@ -22,15 +22,15 @@ - - + + - - - - + + + + diff --git a/SampleERCP/META-INF/MANIFEST.MF b/SampleERCP/META-INF/MANIFEST.MF index c0a156bd..2597b53f 100644 --- a/SampleERCP/META-INF/MANIFEST.MF +++ b/SampleERCP/META-INF/MANIFEST.MF @@ -1,7 +1,7 @@ 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", @@ -12,10 +12,9 @@ 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 diff --git a/SampleERCP/SampleERCP.product b/SampleERCP/SampleERCP.product deleted file mode 100644 index 5d61b95a..00000000 --- a/SampleERCP/SampleERCP.product +++ /dev/null @@ -1,178 +0,0 @@ - - - - - - - - Example - - - - - - - - -clearPersistedState - - -XstartOnFirstThread -Dorg.eclipse.swt.internal.carbon.smallFonts - - - - - - - - - - - - - - - - - org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11 - org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11 - org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11 - - - - https://www.eclipse.org/legal/epl-2.0/ - - 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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SampleERCP/mograsim.rcp.product b/SampleERCP/mograsim.rcp.product new file mode 100644 index 00000000..3614ce7d --- /dev/null +++ b/SampleERCP/mograsim.rcp.product @@ -0,0 +1,149 @@ + + + + + + + + Example + + + + + + + + -clearPersistedState + + -XstartOnFirstThread -Dorg.eclipse.swt.internal.carbon.smallFonts + + + + + + + + + + + + + + + + + + org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11 + org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11 + org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11 + + + + https://www.eclipse.org/legal/epl-2.0/ + + 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SampleERCP/plugin.xml b/SampleERCP/plugin.xml index 50915730..06832aae 100644 --- a/SampleERCP/plugin.xml +++ b/SampleERCP/plugin.xml @@ -11,7 +11,7 @@ name="Simulator WIP"> + value="platform:/plugin/mograsim.rcp/css/default.css"> + class="mograsim.rcp.splashhandlers.ExtensibleSplashHandler" + id="mograsim.rcp.splashHandlers.extensible"> - - - - + productId="mograsim.rcp.product" + splashId="mograsim.rcp.splashHandlers.extensible"> + point="mograsim.rcp.splashExtension"> 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 diff --git a/SampleERCP/src/mograsim/rcp/parts/SamplePart.java b/SampleERCP/src/mograsim/rcp/parts/SamplePart.java new file mode 100644 index 00000000..f37ffb72 --- /dev/null +++ b/SampleERCP/src/mograsim/rcp/parts/SamplePart.java @@ -0,0 +1,61 @@ +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 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 diff --git a/SampleERCP/src/mograsim/rcp/splashhandlers/ExtensibleSplashHandler.java b/SampleERCP/src/mograsim/rcp/splashhandlers/ExtensibleSplashHandler.java new file mode 100644 index 00000000..d32258b2 --- /dev/null +++ b/SampleERCP/src/mograsim/rcp/splashhandlers/ExtensibleSplashHandler.java @@ -0,0 +1,328 @@ +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 fImageList; + + private ArrayList 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 imageIterator = fImageList.iterator(); + Iterator 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 iterator = fImageList.iterator(); + while (iterator.hasNext()) + { + Image image = iterator.next(); + image.dispose(); + } + } +} diff --git a/SampleERCP/src/sampleercp/handlers/AboutHandler.java b/SampleERCP/src/sampleercp/handlers/AboutHandler.java deleted file mode 100644 index f052b90c..00000000 --- a/SampleERCP/src/sampleercp/handlers/AboutHandler.java +++ /dev/null @@ -1,14 +0,0 @@ -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"); - } -} diff --git a/SampleERCP/src/sampleercp/handlers/OpenHandler.java b/SampleERCP/src/sampleercp/handlers/OpenHandler.java deleted file mode 100644 index 15818556..00000000 --- a/SampleERCP/src/sampleercp/handlers/OpenHandler.java +++ /dev/null @@ -1,16 +0,0 @@ -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(); - } -} diff --git a/SampleERCP/src/sampleercp/handlers/QuitHandler.java b/SampleERCP/src/sampleercp/handlers/QuitHandler.java deleted file mode 100644 index 7f40f8c5..00000000 --- a/SampleERCP/src/sampleercp/handlers/QuitHandler.java +++ /dev/null @@ -1,18 +0,0 @@ -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(); - } - } -} diff --git a/SampleERCP/src/sampleercp/handlers/SaveHandler.java b/SampleERCP/src/sampleercp/handlers/SaveHandler.java deleted file mode 100644 index c38f133c..00000000 --- a/SampleERCP/src/sampleercp/handlers/SaveHandler.java +++ /dev/null @@ -1,25 +0,0 @@ -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 diff --git a/SampleERCP/src/sampleercp/parts/LogicUIPart.java b/SampleERCP/src/sampleercp/parts/LogicUIPart.java deleted file mode 100644 index 98fb64c1..00000000 --- a/SampleERCP/src/sampleercp/parts/LogicUIPart.java +++ /dev/null @@ -1,54 +0,0 @@ -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 diff --git a/SampleERCP/src/sampleercp/parts/SamplePart.java b/SampleERCP/src/sampleercp/parts/SamplePart.java deleted file mode 100644 index 93f3502d..00000000 --- a/SampleERCP/src/sampleercp/parts/SamplePart.java +++ /dev/null @@ -1,61 +0,0 @@ -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 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 diff --git a/SampleERCP/src/sampleercp/splashhandlers/ExtensibleSplashHandler.java b/SampleERCP/src/sampleercp/splashhandlers/ExtensibleSplashHandler.java deleted file mode 100644 index 6581eb16..00000000 --- a/SampleERCP/src/sampleercp/splashhandlers/ExtensibleSplashHandler.java +++ /dev/null @@ -1,328 +0,0 @@ -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 fImageList; - - private ArrayList 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 imageIterator = fImageList.iterator(); - Iterator 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 iterator = fImageList.iterator(); - while (iterator.hasNext()) - { - Image image = iterator.next(); - image.dispose(); - } - } -} diff --git a/era.mi/.project b/era.mi/.project index 3b59be06..39cad77d 100644 --- a/era.mi/.project +++ b/era.mi/.project @@ -1,6 +1,6 @@ - era.mi + mograsim.logic.core diff --git a/era.mi/.settings/org.eclipse.jdt.core.prefs b/era.mi/.settings/org.eclipse.jdt.core.prefs index 7ac9c4c1..020d5585 100644 --- a/era.mi/.settings/org.eclipse.jdt.core.prefs +++ b/era.mi/.settings/org.eclipse.jdt.core.prefs @@ -9,6 +9,7 @@ org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nul 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 diff --git a/era.mi/.settings/org.eclipse.jdt.ui.prefs b/era.mi/.settings/org.eclipse.jdt.ui.prefs index e383c7ed..94658822 100644 --- a/era.mi/.settings/org.eclipse.jdt.ui.prefs +++ b/era.mi/.settings/org.eclipse.jdt.ui.prefs @@ -1,6 +1,6 @@ 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 diff --git a/era.mi/META-INF/MANIFEST.MF b/era.mi/META-INF/MANIFEST.MF index 47ff164b..fad3aa8a 100644 --- a/era.mi/META-INF/MANIFEST.MF +++ b/era.mi/META-INF/MANIFEST.MF @@ -1,14 +1,14 @@ 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 diff --git a/era.mi/src/era/mi/logic/Util.java b/era.mi/src/era/mi/logic/Util.java deleted file mode 100644 index d621a44c..00000000 --- a/era.mi/src/era/mi/logic/Util.java +++ /dev/null @@ -1,110 +0,0 @@ -package era.mi.logic; - -import java.util.Arrays; - -import era.mi.logic.types.Bit; - -public final class Util -{ - - @SuppressWarnings("unchecked") - public static 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[][] 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); - } -} diff --git a/era.mi/src/era/mi/logic/components/BasicComponent.java b/era.mi/src/era/mi/logic/components/BasicComponent.java deleted file mode 100644 index 47a3057a..00000000 --- a/era.mi/src/era/mi/logic/components/BasicComponent.java +++ /dev/null @@ -1,36 +0,0 @@ -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(); -} diff --git a/era.mi/src/era/mi/logic/components/BitDisplay.java b/era.mi/src/era/mi/logic/components/BitDisplay.java deleted file mode 100644 index d8691f3b..00000000 --- a/era.mi/src/era/mi/logic/components/BitDisplay.java +++ /dev/null @@ -1,51 +0,0 @@ -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 getAllInputs() - { - return List.of(in); - } - - @Override - public List getAllOutputs() - { - return List.of(); - } -} diff --git a/era.mi/src/era/mi/logic/components/Clock.java b/era.mi/src/era/mi/logic/components/Clock.java deleted file mode 100644 index 01795e01..00000000 --- a/era.mi/src/era/mi/logic/components/Clock.java +++ /dev/null @@ -1,61 +0,0 @@ -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 getAllInputs() - { - return List.of(); - } - - @Override - public List getAllOutputs() - { - return List.of(out); - } -} diff --git a/era.mi/src/era/mi/logic/components/Component.java b/era.mi/src/era/mi/logic/components/Component.java deleted file mode 100644 index e26b6cd3..00000000 --- a/era.mi/src/era/mi/logic/components/Component.java +++ /dev/null @@ -1,28 +0,0 @@ -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 getAllInputs(); - - /** - * Returns immutable list of all outputs to the {@link Component}. Intended for visualization in the UI. - */ - public abstract List getAllOutputs(); -} diff --git a/era.mi/src/era/mi/logic/components/Connector.java b/era.mi/src/era/mi/logic/components/Connector.java deleted file mode 100644 index 5fd7ee4c..00000000 --- a/era.mi/src/era/mi/logic/components/Connector.java +++ /dev/null @@ -1,76 +0,0 @@ -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 getAllInputs() - { - return List.of(a, b); - } - - @Override - public List getAllOutputs() - { - return List.of(a, b); - } -} diff --git a/era.mi/src/era/mi/logic/components/Demux.java b/era.mi/src/era/mi/logic/components/Demux.java deleted file mode 100644 index 3b267dfe..00000000 --- a/era.mi/src/era/mi/logic/components/Demux.java +++ /dev/null @@ -1,82 +0,0 @@ -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 getAllInputs() - { - return List.of(in, select); - } - - @Override - public List getAllOutputs() - { - return List.of(outputs); - } -} diff --git a/era.mi/src/era/mi/logic/components/ManualSwitch.java b/era.mi/src/era/mi/logic/components/ManualSwitch.java deleted file mode 100644 index 9ae08eba..00000000 --- a/era.mi/src/era/mi/logic/components/ManualSwitch.java +++ /dev/null @@ -1,74 +0,0 @@ -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 getAllInputs() - { - return List.of(); - } - - @Override - public List getAllOutputs() - { - return List.of(output); - } - -} diff --git a/era.mi/src/era/mi/logic/components/Merger.java b/era.mi/src/era/mi/logic/components/Merger.java deleted file mode 100644 index ab380665..00000000 --- a/era.mi/src/era/mi/logic/components/Merger.java +++ /dev/null @@ -1,85 +0,0 @@ -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 getAllInputs() - { - return List.of(inputs); - } - - @Override - public List getAllOutputs() - { - return List.of(out); - } -} diff --git a/era.mi/src/era/mi/logic/components/Mux.java b/era.mi/src/era/mi/logic/components/Mux.java deleted file mode 100644 index 5391e655..00000000 --- a/era.mi/src/era/mi/logic/components/Mux.java +++ /dev/null @@ -1,95 +0,0 @@ -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 getAllInputs() - { - ArrayList wires = new ArrayList(Arrays.asList(inputs)); - wires.add(select); - return Collections.unmodifiableList(wires); - } - - @Override - public List getAllOutputs() - { - return List.of(out); - } -} diff --git a/era.mi/src/era/mi/logic/components/Splitter.java b/era.mi/src/era/mi/logic/components/Splitter.java deleted file mode 100644 index 8c54d0f9..00000000 --- a/era.mi/src/era/mi/logic/components/Splitter.java +++ /dev/null @@ -1,59 +0,0 @@ -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 getAllInputs() - { - return List.of(input); - } - - @Override - public List getAllOutputs() - { - return List.of(outputs); - } -} diff --git a/era.mi/src/era/mi/logic/components/TriStateBuffer.java b/era.mi/src/era/mi/logic/components/TriStateBuffer.java deleted file mode 100644 index 123e2c54..00000000 --- a/era.mi/src/era/mi/logic/components/TriStateBuffer.java +++ /dev/null @@ -1,51 +0,0 @@ -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 getAllInputs() - { - return List.of(in, enable); - } - - @Override - public List getAllOutputs() - { - return List.of(out); - } - -} diff --git a/era.mi/src/era/mi/logic/components/gates/AndGate.java b/era.mi/src/era/mi/logic/components/gates/AndGate.java deleted file mode 100644 index edd34d70..00000000 --- a/era.mi/src/era/mi/logic/components/gates/AndGate.java +++ /dev/null @@ -1,14 +0,0 @@ -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); - } -} diff --git a/era.mi/src/era/mi/logic/components/gates/MultiInputGate.java b/era.mi/src/era/mi/logic/components/gates/MultiInputGate.java deleted file mode 100644 index 038e2272..00000000 --- a/era.mi/src/era/mi/logic/components/gates/MultiInputGate.java +++ /dev/null @@ -1,56 +0,0 @@ -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 getAllInputs() - { - return List.of(in); - } - - @Override - public List 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()); - } -} diff --git a/era.mi/src/era/mi/logic/components/gates/NotGate.java b/era.mi/src/era/mi/logic/components/gates/NotGate.java deleted file mode 100644 index 685bc229..00000000 --- a/era.mi/src/era/mi/logic/components/gates/NotGate.java +++ /dev/null @@ -1,50 +0,0 @@ -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 getAllInputs() - { - return List.of(in); - } - - @Override - public List getAllOutputs() - { - return List.of(out); - } -} diff --git a/era.mi/src/era/mi/logic/components/gates/OrGate.java b/era.mi/src/era/mi/logic/components/gates/OrGate.java deleted file mode 100644 index f34206fa..00000000 --- a/era.mi/src/era/mi/logic/components/gates/OrGate.java +++ /dev/null @@ -1,14 +0,0 @@ -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); - } -} diff --git a/era.mi/src/era/mi/logic/components/gates/XorGate.java b/era.mi/src/era/mi/logic/components/gates/XorGate.java deleted file mode 100644 index a177695e..00000000 --- a/era.mi/src/era/mi/logic/components/gates/XorGate.java +++ /dev/null @@ -1,20 +0,0 @@ -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); - } - -} diff --git a/era.mi/src/era/mi/logic/tests/ComponentTest.java b/era.mi/src/era/mi/logic/tests/ComponentTest.java deleted file mode 100644 index 0e2b3460..00000000 --- a/era.mi/src/era/mi/logic/tests/ComponentTest.java +++ /dev/null @@ -1,433 +0,0 @@ -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()); - } -} diff --git a/era.mi/src/era/mi/logic/tests/GUITest.java b/era.mi/src/era/mi/logic/tests/GUITest.java deleted file mode 100644 index 0581812b..00000000 --- a/era.mi/src/era/mi/logic/tests/GUITest.java +++ /dev/null @@ -1,301 +0,0 @@ -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 switchMap = new HashMap<>(); - - int height; - int width; - boolean sizeChanged; - - public GUITest() - { - addMouseListener(new MouseListener() - { - - @Override - public void mouseReleased(MouseEvent e) - { - for (Entry dim : switchMap.entrySet()) - { - if (dim.getValue().contains(e.getPoint())) - { - dim.getKey().switchOff(); - repaint(); - } - } - } - - @Override - public void mousePressed(MouseEvent e) - { - for (Entry 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 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(); - } - } -} diff --git a/era.mi/src/era/mi/logic/tests/TestBitDisplay.java b/era.mi/src/era/mi/logic/tests/TestBitDisplay.java deleted file mode 100644 index 28dc1cc1..00000000 --- a/era.mi/src/era/mi/logic/tests/TestBitDisplay.java +++ /dev/null @@ -1,47 +0,0 @@ -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()); - } -} diff --git a/era.mi/src/era/mi/logic/timeline/Timeline.java b/era.mi/src/era/mi/logic/timeline/Timeline.java deleted file mode 100644 index c0100632..00000000 --- a/era.mi/src/era/mi/logic/timeline/Timeline.java +++ /dev/null @@ -1,203 +0,0 @@ -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 events; - private LongSupplier time; - private long lastTimeUpdated = 0; - - private final List> eventAddedListener; - - public Timeline(int initCapacity) - { - events = new PriorityQueue(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 - * NOTHING_DONE if the {@link Timeline} was already empty - * EXEC_OUT_OF_TIME if the given maximum time was reached - * EXEC_UNTIL_CONDITION if the condition was met - * EXEC_UNTIL_EMPTY 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 listener) - { - eventAddedListener.add(listener); - } - - public void removeEventAddedListener(Consumer 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 - { - 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 diff --git a/era.mi/src/era/mi/logic/timeline/TimelineEvent.java b/era.mi/src/era/mi/logic/timeline/TimelineEvent.java deleted file mode 100644 index 46decf5f..00000000 --- a/era.mi/src/era/mi/logic/timeline/TimelineEvent.java +++ /dev/null @@ -1,30 +0,0 @@ -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 diff --git a/era.mi/src/era/mi/logic/timeline/TimelineEventHandler.java b/era.mi/src/era/mi/logic/timeline/TimelineEventHandler.java deleted file mode 100644 index 921ad1b5..00000000 --- a/era.mi/src/era/mi/logic/timeline/TimelineEventHandler.java +++ /dev/null @@ -1,7 +0,0 @@ -package era.mi.logic.timeline; - -@FunctionalInterface -public interface TimelineEventHandler -{ - public void handle(TimelineEvent e); -} \ No newline at end of file diff --git a/era.mi/src/era/mi/logic/types/Bit.java b/era.mi/src/era/mi/logic/types/Bit.java deleted file mode 100644 index 6674f9a7..00000000 --- a/era.mi/src/era/mi/logic/types/Bit.java +++ /dev/null @@ -1,132 +0,0 @@ -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 -{ - 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 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 diff --git a/era.mi/src/era/mi/logic/types/BitVector.java b/era.mi/src/era/mi/logic/types/BitVector.java deleted file mode 100644 index 38d7d263..00000000 --- a/era.mi/src/era/mi/logic/types/BitVector.java +++ /dev/null @@ -1,316 +0,0 @@ -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, Iterable, 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 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 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 - { - 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}
- * - * @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 offset to the right.
- * Therefore offset + other.length() <= this.length() needs to be true. - * - * @throws ArrayIndexOutOfBoundsException if offset + other.length() > this.length() - * - * @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 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(); - } - }; - } -} diff --git a/era.mi/src/era/mi/logic/types/BitVectorFormatter.java b/era.mi/src/era/mi/logic/types/BitVectorFormatter.java deleted file mode 100644 index 6dab1d7b..00000000 --- a/era.mi/src/era/mi/logic/types/BitVectorFormatter.java +++ /dev/null @@ -1,53 +0,0 @@ -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 diff --git a/era.mi/src/era/mi/logic/types/ColorDefinition.java b/era.mi/src/era/mi/logic/types/ColorDefinition.java deleted file mode 100644 index b9e851fd..00000000 --- a/era.mi/src/era/mi/logic/types/ColorDefinition.java +++ /dev/null @@ -1,54 +0,0 @@ -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). - *

- * A {@link ColorDefinition} is defined either by a {@link BuiltInColor} constant, in which case r==g==b==-1, or by red / green - * / blue components, in which case builtInColor==null - */ -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 diff --git a/era.mi/src/era/mi/logic/types/LogicType.java b/era.mi/src/era/mi/logic/types/LogicType.java deleted file mode 100644 index 9a3180e7..00000000 --- a/era.mi/src/era/mi/logic/types/LogicType.java +++ /dev/null @@ -1,130 +0,0 @@ -package era.mi.logic.types; - -/** - * Interface for types that support the basic logic operations - * - * @author Christian Femers - * - * @param the logic type itself, to make the operations closed in T - * @param the operand type, may be the same as T, see {@link StrictLogicType} - */ -public interface LogicType, S> -{ - /** - * Determines the result when two signals meet each other directly, also called resolution (IEEE 1164) - * - * For example: - * - *

-	 * 0 joined 0 == 0
-	 * 1 joined 0 == X
-	 * 0 joined 1 == X
-	 * 1 joined 1 == 1
-	 * 
- * - * @param t the second logic signal - * @return the resulting value - * @author Christian Femers - */ - T join(S t); - - /** - * Classical logic AND - * - * For example: - * - *
-	 * 0 AND 0 == 0
-	 * 1 AND 0 == 0
-	 * 0 AND 1 == 0
-	 * 1 AND 1 == 1
-	 * 
- * - * @param t the second logic signal - * @return the resulting value - * @author Christian Femers - */ - T and(S t); - - /** - * Classical logic OR - * - * For example: - * - *
-	 * 0 OR 0 == 0
-	 * 1 OR 0 == 1
-	 * 0 OR 1 == 1
-	 * 1 OR 1 == 1
-	 * 
- * - * @param t the second logic signal - * @return the resulting value - * @author Christian Femers - */ - T or(S t); - - /** - * Classical logic XOR (exclusive OR) - * - * For example: - * - *
-	 * 0 XOR 0 == 0
-	 * 1 XOR 0 == 1
-	 * 0 XOR 1 == 1
-	 * 1 XOR 1 == 0
-	 * 
- * - * @param t the second logic signal - * @return the resulting value - * @author Christian Femers - */ - T xor(S t); - - /** - * Classical logic NOT (logical negation) - * - * For example: - * - *
-	 * NOT 0 == 1
-	 * NOT 1 == 0
-	 * 
- * - * @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}
- * Used to determine equality (alias parity, consistency) - * - * @author Christian Femers - */ - default T xnor(S t) - { - return xor(t).not(); - } -} diff --git a/era.mi/src/era/mi/logic/types/MutationOperation.java b/era.mi/src/era/mi/logic/types/MutationOperation.java deleted file mode 100644 index 2ed9dc28..00000000 --- a/era.mi/src/era/mi/logic/types/MutationOperation.java +++ /dev/null @@ -1,11 +0,0 @@ -package era.mi.logic.types; - -import java.util.function.BiFunction; - -import era.mi.logic.types.BitVector.BitVectorMutator; - -@FunctionalInterface -public interface MutationOperation extends BiFunction -{ - // no changes necessary, only for convenience and readability -} diff --git a/era.mi/src/era/mi/logic/types/StrictLogicType.java b/era.mi/src/era/mi/logic/types/StrictLogicType.java deleted file mode 100644 index 560bb96f..00000000 --- a/era.mi/src/era/mi/logic/types/StrictLogicType.java +++ /dev/null @@ -1,14 +0,0 @@ -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 the logic type itself to make the operations closed - */ -public interface StrictLogicType> extends LogicType -{ - // this is just a matter of type parameters -} diff --git a/era.mi/src/era/mi/logic/wires/Wire.java b/era.mi/src/era/mi/logic/wires/Wire.java deleted file mode 100644 index 9a173877..00000000 --- a/era.mi/src/era/mi/logic/wires/Wire.java +++ /dev/null @@ -1,492 +0,0 @@ -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 attached = new ArrayList(); - public final int length; - private List inputs = new ArrayList(); - 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 true if all bits are either Bit.ONE or Bit.ZERO (they do not all have to have the same - * value), not Bit.X or Bit.Z. false 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 observers = new ArrayList(); - - 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 true if all bits are either Bit.ONE or Bit.ZERO (they do not all have to have the - * same value), not Bit.X or Bit.Z. false 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 diff --git a/era.mi/src/era/mi/logic/wires/WireObserver.java b/era.mi/src/era/mi/logic/wires/WireObserver.java deleted file mode 100644 index 4c4bce0b..00000000 --- a/era.mi/src/era/mi/logic/wires/WireObserver.java +++ /dev/null @@ -1,9 +0,0 @@ -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); -} diff --git a/era.mi/src/mograsim/logic/core/Util.java b/era.mi/src/mograsim/logic/core/Util.java new file mode 100644 index 00000000..5f88e834 --- /dev/null +++ b/era.mi/src/mograsim/logic/core/Util.java @@ -0,0 +1,110 @@ +package mograsim.logic.core; + +import java.util.Arrays; + +import mograsim.logic.core.types.Bit; + +public final class Util +{ + + @SuppressWarnings("unchecked") + public static 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[][] 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); + } +} diff --git a/era.mi/src/mograsim/logic/core/components/BasicComponent.java b/era.mi/src/mograsim/logic/core/components/BasicComponent.java new file mode 100644 index 00000000..720c1259 --- /dev/null +++ b/era.mi/src/mograsim/logic/core/components/BasicComponent.java @@ -0,0 +1,36 @@ +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(); +} diff --git a/era.mi/src/mograsim/logic/core/components/BitDisplay.java b/era.mi/src/mograsim/logic/core/components/BitDisplay.java new file mode 100644 index 00000000..0d398dab --- /dev/null +++ b/era.mi/src/mograsim/logic/core/components/BitDisplay.java @@ -0,0 +1,51 @@ +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 getAllInputs() + { + return List.of(in); + } + + @Override + public List getAllOutputs() + { + return List.of(); + } +} diff --git a/era.mi/src/mograsim/logic/core/components/Clock.java b/era.mi/src/mograsim/logic/core/components/Clock.java new file mode 100644 index 00000000..4b15ed38 --- /dev/null +++ b/era.mi/src/mograsim/logic/core/components/Clock.java @@ -0,0 +1,61 @@ +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 getAllInputs() + { + return List.of(); + } + + @Override + public List getAllOutputs() + { + return List.of(out); + } +} diff --git a/era.mi/src/mograsim/logic/core/components/Component.java b/era.mi/src/mograsim/logic/core/components/Component.java new file mode 100644 index 00000000..54109df7 --- /dev/null +++ b/era.mi/src/mograsim/logic/core/components/Component.java @@ -0,0 +1,28 @@ +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 getAllInputs(); + + /** + * Returns immutable list of all outputs to the {@link Component}. Intended for visualization in the UI. + */ + public abstract List getAllOutputs(); +} diff --git a/era.mi/src/mograsim/logic/core/components/Connector.java b/era.mi/src/mograsim/logic/core/components/Connector.java new file mode 100644 index 00000000..0972cfad --- /dev/null +++ b/era.mi/src/mograsim/logic/core/components/Connector.java @@ -0,0 +1,76 @@ +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 getAllInputs() + { + return List.of(a, b); + } + + @Override + public List getAllOutputs() + { + return List.of(a, b); + } +} diff --git a/era.mi/src/mograsim/logic/core/components/Demux.java b/era.mi/src/mograsim/logic/core/components/Demux.java new file mode 100644 index 00000000..f1efb726 --- /dev/null +++ b/era.mi/src/mograsim/logic/core/components/Demux.java @@ -0,0 +1,82 @@ +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 getAllInputs() + { + return List.of(in, select); + } + + @Override + public List getAllOutputs() + { + return List.of(outputs); + } +} diff --git a/era.mi/src/mograsim/logic/core/components/ManualSwitch.java b/era.mi/src/mograsim/logic/core/components/ManualSwitch.java new file mode 100644 index 00000000..5012761e --- /dev/null +++ b/era.mi/src/mograsim/logic/core/components/ManualSwitch.java @@ -0,0 +1,74 @@ +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 getAllInputs() + { + return List.of(); + } + + @Override + public List getAllOutputs() + { + return List.of(output); + } + +} diff --git a/era.mi/src/mograsim/logic/core/components/Merger.java b/era.mi/src/mograsim/logic/core/components/Merger.java new file mode 100644 index 00000000..114dbe7b --- /dev/null +++ b/era.mi/src/mograsim/logic/core/components/Merger.java @@ -0,0 +1,85 @@ +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 getAllInputs() + { + return List.of(inputs); + } + + @Override + public List getAllOutputs() + { + return List.of(out); + } +} diff --git a/era.mi/src/mograsim/logic/core/components/Mux.java b/era.mi/src/mograsim/logic/core/components/Mux.java new file mode 100644 index 00000000..44c2a1d8 --- /dev/null +++ b/era.mi/src/mograsim/logic/core/components/Mux.java @@ -0,0 +1,95 @@ +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 getAllInputs() + { + ArrayList wires = new ArrayList(Arrays.asList(inputs)); + wires.add(select); + return Collections.unmodifiableList(wires); + } + + @Override + public List getAllOutputs() + { + return List.of(out); + } +} diff --git a/era.mi/src/mograsim/logic/core/components/Splitter.java b/era.mi/src/mograsim/logic/core/components/Splitter.java new file mode 100644 index 00000000..34749527 --- /dev/null +++ b/era.mi/src/mograsim/logic/core/components/Splitter.java @@ -0,0 +1,59 @@ +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 getAllInputs() + { + return List.of(input); + } + + @Override + public List getAllOutputs() + { + return List.of(outputs); + } +} diff --git a/era.mi/src/mograsim/logic/core/components/TriStateBuffer.java b/era.mi/src/mograsim/logic/core/components/TriStateBuffer.java new file mode 100644 index 00000000..b8ba0650 --- /dev/null +++ b/era.mi/src/mograsim/logic/core/components/TriStateBuffer.java @@ -0,0 +1,51 @@ +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 getAllInputs() + { + return List.of(in, enable); + } + + @Override + public List getAllOutputs() + { + return List.of(out); + } + +} diff --git a/era.mi/src/mograsim/logic/core/components/gates/AndGate.java b/era.mi/src/mograsim/logic/core/components/gates/AndGate.java new file mode 100644 index 00000000..1f02a193 --- /dev/null +++ b/era.mi/src/mograsim/logic/core/components/gates/AndGate.java @@ -0,0 +1,14 @@ +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); + } +} diff --git a/era.mi/src/mograsim/logic/core/components/gates/MultiInputGate.java b/era.mi/src/mograsim/logic/core/components/gates/MultiInputGate.java new file mode 100644 index 00000000..40b10780 --- /dev/null +++ b/era.mi/src/mograsim/logic/core/components/gates/MultiInputGate.java @@ -0,0 +1,56 @@ +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 getAllInputs() + { + return List.of(in); + } + + @Override + public List 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()); + } +} diff --git a/era.mi/src/mograsim/logic/core/components/gates/NotGate.java b/era.mi/src/mograsim/logic/core/components/gates/NotGate.java new file mode 100644 index 00000000..ebad7e3a --- /dev/null +++ b/era.mi/src/mograsim/logic/core/components/gates/NotGate.java @@ -0,0 +1,50 @@ +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 getAllInputs() + { + return List.of(in); + } + + @Override + public List getAllOutputs() + { + return List.of(out); + } +} diff --git a/era.mi/src/mograsim/logic/core/components/gates/OrGate.java b/era.mi/src/mograsim/logic/core/components/gates/OrGate.java new file mode 100644 index 00000000..69d64111 --- /dev/null +++ b/era.mi/src/mograsim/logic/core/components/gates/OrGate.java @@ -0,0 +1,14 @@ +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); + } +} diff --git a/era.mi/src/mograsim/logic/core/components/gates/XorGate.java b/era.mi/src/mograsim/logic/core/components/gates/XorGate.java new file mode 100644 index 00000000..ea960364 --- /dev/null +++ b/era.mi/src/mograsim/logic/core/components/gates/XorGate.java @@ -0,0 +1,20 @@ +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); + } + +} diff --git a/era.mi/src/mograsim/logic/core/tests/ComponentTest.java b/era.mi/src/mograsim/logic/core/tests/ComponentTest.java new file mode 100644 index 00000000..c080d1a7 --- /dev/null +++ b/era.mi/src/mograsim/logic/core/tests/ComponentTest.java @@ -0,0 +1,433 @@ +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()); + } +} diff --git a/era.mi/src/mograsim/logic/core/tests/GUITest.java b/era.mi/src/mograsim/logic/core/tests/GUITest.java new file mode 100644 index 00000000..0a0929c2 --- /dev/null +++ b/era.mi/src/mograsim/logic/core/tests/GUITest.java @@ -0,0 +1,301 @@ +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 switchMap = new HashMap<>(); + + int height; + int width; + boolean sizeChanged; + + public GUITest() + { + addMouseListener(new MouseListener() + { + + @Override + public void mouseReleased(MouseEvent e) + { + for (Entry dim : switchMap.entrySet()) + { + if (dim.getValue().contains(e.getPoint())) + { + dim.getKey().switchOff(); + repaint(); + } + } + } + + @Override + public void mousePressed(MouseEvent e) + { + for (Entry 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 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(); + } + } +} diff --git a/era.mi/src/mograsim/logic/core/tests/TestBitDisplay.java b/era.mi/src/mograsim/logic/core/tests/TestBitDisplay.java new file mode 100644 index 00000000..80eef5fb --- /dev/null +++ b/era.mi/src/mograsim/logic/core/tests/TestBitDisplay.java @@ -0,0 +1,47 @@ +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()); + } +} diff --git a/era.mi/src/mograsim/logic/core/timeline/Timeline.java b/era.mi/src/mograsim/logic/core/timeline/Timeline.java new file mode 100644 index 00000000..1a380b41 --- /dev/null +++ b/era.mi/src/mograsim/logic/core/timeline/Timeline.java @@ -0,0 +1,203 @@ +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 events; + private LongSupplier time; + private long lastTimeUpdated = 0; + + private final List> eventAddedListener; + + public Timeline(int initCapacity) + { + events = new PriorityQueue(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 + * NOTHING_DONE if the {@link Timeline} was already empty + * EXEC_OUT_OF_TIME if the given maximum time was reached + * EXEC_UNTIL_CONDITION if the condition was met + * EXEC_UNTIL_EMPTY 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 listener) + { + eventAddedListener.add(listener); + } + + public void removeEventAddedListener(Consumer 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 + { + 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 diff --git a/era.mi/src/mograsim/logic/core/timeline/TimelineEvent.java b/era.mi/src/mograsim/logic/core/timeline/TimelineEvent.java new file mode 100644 index 00000000..6372bde8 --- /dev/null +++ b/era.mi/src/mograsim/logic/core/timeline/TimelineEvent.java @@ -0,0 +1,30 @@ +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 diff --git a/era.mi/src/mograsim/logic/core/timeline/TimelineEventHandler.java b/era.mi/src/mograsim/logic/core/timeline/TimelineEventHandler.java new file mode 100644 index 00000000..dd58c0b3 --- /dev/null +++ b/era.mi/src/mograsim/logic/core/timeline/TimelineEventHandler.java @@ -0,0 +1,7 @@ +package mograsim.logic.core.timeline; + +@FunctionalInterface +public interface TimelineEventHandler +{ + public void handle(TimelineEvent e); +} \ No newline at end of file diff --git a/era.mi/src/mograsim/logic/core/types/Bit.java b/era.mi/src/mograsim/logic/core/types/Bit.java new file mode 100644 index 00000000..60c4e192 --- /dev/null +++ b/era.mi/src/mograsim/logic/core/types/Bit.java @@ -0,0 +1,132 @@ +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 +{ + 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 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 diff --git a/era.mi/src/mograsim/logic/core/types/BitVector.java b/era.mi/src/mograsim/logic/core/types/BitVector.java new file mode 100644 index 00000000..2d28b2ce --- /dev/null +++ b/era.mi/src/mograsim/logic/core/types/BitVector.java @@ -0,0 +1,316 @@ +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, Iterable, 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 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 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 + { + 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}
+ * + * @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 offset to the right.
+ * Therefore offset + other.length() <= this.length() needs to be true. + * + * @throws ArrayIndexOutOfBoundsException if offset + other.length() > this.length() + * + * @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 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(); + } + }; + } +} diff --git a/era.mi/src/mograsim/logic/core/types/BitVectorFormatter.java b/era.mi/src/mograsim/logic/core/types/BitVectorFormatter.java new file mode 100644 index 00000000..9a61c11a --- /dev/null +++ b/era.mi/src/mograsim/logic/core/types/BitVectorFormatter.java @@ -0,0 +1,53 @@ +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 diff --git a/era.mi/src/mograsim/logic/core/types/ColorDefinition.java b/era.mi/src/mograsim/logic/core/types/ColorDefinition.java new file mode 100644 index 00000000..7d69837a --- /dev/null +++ b/era.mi/src/mograsim/logic/core/types/ColorDefinition.java @@ -0,0 +1,54 @@ +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). + *

+ * A {@link ColorDefinition} is defined either by a {@link BuiltInColor} constant, in which case r==g==b==-1, or by red / green + * / blue components, in which case builtInColor==null + */ +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 diff --git a/era.mi/src/mograsim/logic/core/types/LogicType.java b/era.mi/src/mograsim/logic/core/types/LogicType.java new file mode 100644 index 00000000..b4e4efbc --- /dev/null +++ b/era.mi/src/mograsim/logic/core/types/LogicType.java @@ -0,0 +1,130 @@ +package mograsim.logic.core.types; + +/** + * Interface for types that support the basic logic operations + * + * @author Christian Femers + * + * @param the logic type itself, to make the operations closed in T + * @param the operand type, may be the same as T, see {@link StrictLogicType} + */ +public interface LogicType, S> +{ + /** + * Determines the result when two signals meet each other directly, also called resolution (IEEE 1164) + * + * For example: + * + *

+	 * 0 joined 0 == 0
+	 * 1 joined 0 == X
+	 * 0 joined 1 == X
+	 * 1 joined 1 == 1
+	 * 
+ * + * @param t the second logic signal + * @return the resulting value + * @author Christian Femers + */ + T join(S t); + + /** + * Classical logic AND + * + * For example: + * + *
+	 * 0 AND 0 == 0
+	 * 1 AND 0 == 0
+	 * 0 AND 1 == 0
+	 * 1 AND 1 == 1
+	 * 
+ * + * @param t the second logic signal + * @return the resulting value + * @author Christian Femers + */ + T and(S t); + + /** + * Classical logic OR + * + * For example: + * + *
+	 * 0 OR 0 == 0
+	 * 1 OR 0 == 1
+	 * 0 OR 1 == 1
+	 * 1 OR 1 == 1
+	 * 
+ * + * @param t the second logic signal + * @return the resulting value + * @author Christian Femers + */ + T or(S t); + + /** + * Classical logic XOR (exclusive OR) + * + * For example: + * + *
+	 * 0 XOR 0 == 0
+	 * 1 XOR 0 == 1
+	 * 0 XOR 1 == 1
+	 * 1 XOR 1 == 0
+	 * 
+ * + * @param t the second logic signal + * @return the resulting value + * @author Christian Femers + */ + T xor(S t); + + /** + * Classical logic NOT (logical negation) + * + * For example: + * + *
+	 * NOT 0 == 1
+	 * NOT 1 == 0
+	 * 
+ * + * @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}
+ * Used to determine equality (alias parity, consistency) + * + * @author Christian Femers + */ + default T xnor(S t) + { + return xor(t).not(); + } +} diff --git a/era.mi/src/mograsim/logic/core/types/MutationOperation.java b/era.mi/src/mograsim/logic/core/types/MutationOperation.java new file mode 100644 index 00000000..998e5979 --- /dev/null +++ b/era.mi/src/mograsim/logic/core/types/MutationOperation.java @@ -0,0 +1,11 @@ +package mograsim.logic.core.types; + +import java.util.function.BiFunction; + +import mograsim.logic.core.types.BitVector.BitVectorMutator; + +@FunctionalInterface +public interface MutationOperation extends BiFunction +{ + // no changes necessary, only for convenience and readability +} diff --git a/era.mi/src/mograsim/logic/core/types/StrictLogicType.java b/era.mi/src/mograsim/logic/core/types/StrictLogicType.java new file mode 100644 index 00000000..91cb10f2 --- /dev/null +++ b/era.mi/src/mograsim/logic/core/types/StrictLogicType.java @@ -0,0 +1,14 @@ +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 the logic type itself to make the operations closed + */ +public interface StrictLogicType> extends LogicType +{ + // this is just a matter of type parameters +} diff --git a/era.mi/src/mograsim/logic/core/wires/Wire.java b/era.mi/src/mograsim/logic/core/wires/Wire.java new file mode 100644 index 00000000..746e9269 --- /dev/null +++ b/era.mi/src/mograsim/logic/core/wires/Wire.java @@ -0,0 +1,492 @@ +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 attached = new ArrayList(); + public final int length; + private List inputs = new ArrayList(); + 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 true if all bits are either Bit.ONE or Bit.ZERO (they do not all have to have the same + * value), not Bit.X or Bit.Z. false 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 observers = new ArrayList(); + + 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 true if all bits are either Bit.ONE or Bit.ZERO (they do not all have to have the + * same value), not Bit.X or Bit.Z. false 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 diff --git a/era.mi/src/mograsim/logic/core/wires/WireObserver.java b/era.mi/src/mograsim/logic/core/wires/WireObserver.java new file mode 100644 index 00000000..2082bd34 --- /dev/null +++ b/era.mi/src/mograsim/logic/core/wires/WireObserver.java @@ -0,0 +1,9 @@ +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); +}