From 69ec19d54ceb6d5abbb8b4faa55284af22174859 Mon Sep 17 00:00:00 2001 From: Daniel Kirschten Date: Wed, 2 Oct 2019 16:04:33 +0200 Subject: [PATCH] HighLevelStates now support adding/removing listeners --- .../mograsim/logic/model/LogicUICanvas.java | 74 +++++++++++++-- .../model/components/ModelComponent.java | 18 +++- .../model/components/atomic/ModelClock.java | 55 ++++++++++- .../components/atomic/ModelManualSwitch.java | 59 ++++++++++-- ...pleRectangularHardcodedModelComponent.java | 67 +++++++++++--- .../logic/model/model/wires/ModelWire.java | 22 +++++ ...angularHardcodedModelComponentAdapter.java | 4 +- .../model/snippets/HighLevelStateHandler.java | 13 ++- .../DefaultHighLevelStateHandler.java | 18 +++- .../StandardHighLevelStateHandler.java | 47 +++++++++- .../atomic/AtomicHighLevelStateHandler.java | 6 ++ ...rSplittingAtomicHighLevelStateHandler.java | 34 +++++++ ...DelegatingAtomicHighLevelStateHandler.java | 29 +++++- ...ireForcingAtomicHighLevelStateHandler.java | 41 +++++++++ ...tingSubcomponentHighLevelStateHandler.java | 29 +++++- .../SubcomponentHighLevelStateHandler.java | 6 ++ .../model/util/ObservableAtomicReference.java | 92 +++++++++++++++++++ .../src/net/mograsim/machine/Machine.java | 1 + .../ModelMicroInstructionMemory.java | 32 ++++++- .../memory/ModelWordAddressableMemory.java | 32 ++++++- 20 files changed, 619 insertions(+), 60 deletions(-) create mode 100644 plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/util/ObservableAtomicReference.java diff --git a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/LogicUICanvas.java b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/LogicUICanvas.java index 34cfb099..fe6c14cc 100644 --- a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/LogicUICanvas.java +++ b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/LogicUICanvas.java @@ -1,9 +1,10 @@ package net.mograsim.logic.model; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import org.eclipse.swt.SWT; @@ -89,17 +90,19 @@ public class LogicUICanvas extends ZoomableCanvas List componentsByItemIndex = new ArrayList<>(); List models = new ArrayList<>(); AtomicBoolean recalculateQueued = new AtomicBoolean(); - AtomicReference> compAdded = new AtomicReference<>(); - AtomicReference> compRemoved = new AtomicReference<>(); - compAdded.set(c -> compsChanged(compAdded.get(), compRemoved.get(), c, models, componentsByItemIndex, componentSelector, model, - recalculateQueued, true)); - compRemoved.set(c -> compsChanged(compAdded.get(), compRemoved.get(), c, models, componentsByItemIndex, componentSelector, model, - recalculateQueued, false)); - iterateModelTree(compAdded.get(), compRemoved.get(), model, models, true); + @SuppressWarnings("unchecked") + Consumer[] compAdded = new Consumer[1]; + @SuppressWarnings("unchecked") + Consumer[] compRemoved = new Consumer[1]; + compAdded[0] = c -> compsChanged(compAdded[0], compRemoved[0], c, models, componentsByItemIndex, componentSelector, model, + recalculateQueued, true); + compRemoved[0] = c -> compsChanged(compAdded[0], compRemoved[0], c, models, componentsByItemIndex, componentSelector, model, + recalculateQueued, false); + iterateModelTree(compAdded[0], compRemoved[0], model, models, true); debugShell.addListener(SWT.Dispose, e -> models.forEach(m -> { - m.removeComponentAddedListener(compAdded.get()); - m.removeComponentRemovedListener(compRemoved.get()); + m.removeComponentAddedListener(compAdded[0]); + m.removeComponentRemovedListener(compRemoved[0]); })); queueRecalculateComponentSelector(recalculateQueued, componentsByItemIndex, componentSelector, model); new Label(debugShell, SWT.NONE).setText("Target state ID: "); @@ -121,8 +124,12 @@ public class LogicUICanvas extends ZoomableCanvas valueText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); Button send = new Button(debugShell, SWT.PUSH); send.setText("Send!"); + Button addListener = new Button(debugShell, SWT.PUSH); + addListener.setText("Add sysout listener"); Button get = new Button(debugShell, SWT.PUSH); get.setText("Get!"); + Button removeListener = new Button(debugShell, SWT.PUSH); + removeListener.setText("Remove sysout listener"); Text output = new Text(debugShell, SWT.READ_ONLY); output.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1)); Listener sendAction = e -> @@ -167,6 +174,53 @@ public class LogicUICanvas extends ZoomableCanvas valueText.addListener(SWT.DefaultSelection, sendAction); get.addListener(SWT.Selection, getAction); stateIDText.addListener(SWT.DefaultSelection, getAction); + Map>> sysoutListenersPerHLSPerTarget = new HashMap<>(); + addListener.addListener(SWT.Selection, e -> + { + try + { + int componentIndex = componentSelector.getSelectionIndex(); + if (componentIndex < 0 || componentIndex >= componentsByItemIndex.size()) + throw new RuntimeException("No component selected"); + ModelComponent target = componentsByItemIndex.get(componentIndex); + Map> sysoutListenersPerHLS = sysoutListenersPerHLSPerTarget.computeIfAbsent(target, + k -> new HashMap<>()); + String stateIDString = stateIDText.getText(); + if (sysoutListenersPerHLS.containsKey(stateIDString)) + throw new RuntimeException("Listener already registered"); + Consumer sysoutListener = v -> System.out.println(stateIDString + ": " + v); + target.addHighLevelStateListener(stateIDString, sysoutListener); + sysoutListenersPerHLS.put(stateIDString, sysoutListener); + output.setText("Success!"); + } + catch (Exception x) + { + output.setText(x.getClass().getSimpleName() + (x.getMessage() == null ? "" : ": " + x.getMessage())); + } + }); + removeListener.addListener(SWT.Selection, e -> + { + try + { + int componentIndex = componentSelector.getSelectionIndex(); + if (componentIndex < 0 || componentIndex >= componentsByItemIndex.size()) + throw new RuntimeException("No component selected"); + ModelComponent target = componentsByItemIndex.get(componentIndex); + Map> sysoutListenersPerHLS = sysoutListenersPerHLSPerTarget.get(target); + if (sysoutListenersPerHLS == null) + throw new RuntimeException("Listener not registered"); + String stateIDString = stateIDText.getText(); + Consumer sysoutListener = sysoutListenersPerHLS.remove(stateIDString); + if (sysoutListener == null) + throw new RuntimeException("Listener not registered"); + target.removeHighLevelStateListener(stateIDString, sysoutListener); + output.setText("Success!"); + } + catch (Exception x) + { + output.setText(x.getClass().getSimpleName() + (x.getMessage() == null ? "" : ": " + x.getMessage())); + } + }); debugShell.open(); addDisposeListener(e -> debugShell.dispose()); } diff --git a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/model/components/ModelComponent.java b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/model/components/ModelComponent.java index ded66859..b4d89282 100644 --- a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/model/components/ModelComponent.java +++ b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/model/components/ModelComponent.java @@ -215,13 +215,13 @@ public abstract class ModelComponent implements JSONSerializable * See {@link HighLevelStateHandler} for an explanation of high-level state IDs. * * @see #setHighLevelState(String, Object) - * @see HighLevelStateHandler#getHighLevelState(String) + * @see HighLevelStateHandler#get(String) * * @author Daniel Kirschten */ public final Object getHighLevelState(String stateID) { - return highLevelStateHandler.getHighLevelState(stateID); + return highLevelStateHandler.get(stateID); } /** @@ -229,13 +229,23 @@ public abstract class ModelComponent implements JSONSerializable * See {@link HighLevelStateHandler} for an explanation of high-level state IDs. * * @see #getHighLevelState(String) - * @see HighLevelStateHandler#setHighLevelState(String, Object) + * @see HighLevelStateHandler#set(String, Object) * * @author Daniel Kirschten */ public final void setHighLevelState(String stateID, Object newState) { - highLevelStateHandler.setHighLevelState(stateID, newState); + highLevelStateHandler.set(stateID, newState); + } + + public final void addHighLevelStateListener(String stateID, Consumer stateChanged) + { + highLevelStateHandler.addListener(stateID, stateChanged); + } + + public final void removeHighLevelStateListener(String stateID, Consumer stateChanged) + { + highLevelStateHandler.removeListener(stateID, stateChanged); } // "graphical" operations diff --git a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/model/components/atomic/ModelClock.java b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/model/components/atomic/ModelClock.java index 5c54069a..3c4eec61 100644 --- a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/model/components/atomic/ModelClock.java +++ b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/model/components/atomic/ModelClock.java @@ -1,5 +1,9 @@ package net.mograsim.logic.model.model.components.atomic; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + import org.eclipse.swt.graphics.Color; import com.google.gson.JsonSyntaxException; @@ -10,6 +14,7 @@ import net.haspamelodica.swt.helper.swtobjectwrappers.Point; import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle; import net.mograsim.logic.core.LogicObserver; import net.mograsim.logic.core.components.CoreClock; +import net.mograsim.logic.core.types.BitVector; import net.mograsim.logic.model.model.LogicModelModifiable; import net.mograsim.logic.model.model.components.ModelComponent; import net.mograsim.logic.model.model.components.Orientation; @@ -37,6 +42,8 @@ public class ModelClock extends ModelComponent private OrientationCalculator oc; private CoreClock clock; + private final List> hlsListeners; + public ModelClock(LogicModelModifiable model, ModelClockParams params) { this(model, params, null); @@ -46,7 +53,6 @@ public class ModelClock extends ModelComponent { super(model, name, false); this.params = params; - logicObs = (i) -> model.requestRedraw(); oc = new OrientationCalculator(params.orientation, width, height); setSize(oc.width(), oc.height()); @@ -54,16 +60,25 @@ public class ModelClock extends ModelComponent this.outputPin = new Pin(model, this, "", 1, PinUsage.OUTPUT, oc.newX(width, height / 2), oc.newY(width, height / 2)); addPin(outputPin); + this.hlsListeners = new ArrayList<>(); + + logicObs = i -> + { + model.requestRedraw(); + BitVector v = getOutValues(); + hlsListeners.forEach(l -> l.accept(v)); + }; + setHighLevelStateHandler(new HighLevelStateHandler() { @Override - public Object getHighLevelState(String stateID) + public Object get(String stateID) { switch (stateID) { case "out": if (clock != null) - return clock.getOutValues(); + return getOutValues(); return null; default: throw new IllegalArgumentException("No high level state with ID " + stateID); @@ -71,7 +86,7 @@ public class ModelClock extends ModelComponent } @Override - public void setHighLevelState(String stateID, Object newState) + public void set(String stateID, Object newState) { switch (stateID) { @@ -82,6 +97,32 @@ public class ModelClock extends ModelComponent } } + @Override + public void addListener(String stateID, Consumer stateChanged) + { + switch (stateID) + { + case "out": + hlsListeners.add(stateChanged); + break; + default: + throw new IllegalArgumentException("No high level state with ID " + stateID); + } + } + + @Override + public void removeListener(String stateID, java.util.function.Consumer stateChanged) + { + switch (stateID) + { + case "out": + hlsListeners.remove(stateChanged); + break; + default: + throw new IllegalArgumentException("No high level state with ID " + stateID); + } + } + @Override public String getIDForSerializing(IdentifyParams idParams) { @@ -131,6 +172,7 @@ public class ModelClock extends ModelComponent return clock != null; } + // TODO remove public CoreClock getClock() { return clock; @@ -158,6 +200,11 @@ public class ModelClock extends ModelComponent return params; } + private BitVector getOutValues() + { + return clock.getOutValues(); + } + static { LogicCoreAdapter.addComponentAdapter(new ClockAdapter()); diff --git a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/model/components/atomic/ModelManualSwitch.java b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/model/components/atomic/ModelManualSwitch.java index cf9735ae..d4e04376 100644 --- a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/model/components/atomic/ModelManualSwitch.java +++ b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/model/components/atomic/ModelManualSwitch.java @@ -1,5 +1,9 @@ package net.mograsim.logic.model.model.components.atomic; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + import org.eclipse.swt.graphics.Color; import net.haspamelodica.swt.helper.gcs.GeneralGC; @@ -35,6 +39,8 @@ public class ModelManualSwitch extends ModelComponent private final LogicObserver logicObs; private CoreManualSwitch manualSwitch; + private final List> hlsListeners; + public ModelManualSwitch(LogicModelModifiable model, int logicWidth) { this(model, logicWidth, null); @@ -44,21 +50,29 @@ public class ModelManualSwitch extends ModelComponent { super(model, name, false); this.logicWidth = logicWidth; - logicObs = (i) -> model.requestRedraw(); setSize(width, height); addPin(this.outputPin = new Pin(model, this, "", logicWidth, PinUsage.OUTPUT, width, height / 2)); + hlsListeners = new ArrayList<>(); + + logicObs = i -> + { + model.requestRedraw(); + BitVector v = getOutValues(); + hlsListeners.forEach(l -> l.accept(v)); + }; + setHighLevelStateHandler(new HighLevelStateHandler() { @Override - public Object getHighLevelState(String stateID) + public Object get(String stateID) { switch (stateID) { case "out": if (manualSwitch != null) - return manualSwitch.getValues(); + return getOutValues(); return null; default: throw new IllegalArgumentException("No high level state with ID " + stateID); @@ -66,7 +80,7 @@ public class ModelManualSwitch extends ModelComponent } @Override - public void setHighLevelState(String stateID, Object newState) + public void set(String stateID, Object newState) { switch (stateID) { @@ -79,6 +93,32 @@ public class ModelManualSwitch extends ModelComponent } } + @Override + public void addListener(String stateID, Consumer stateChanged) + { + switch (stateID) + { + case "out": + hlsListeners.add(stateChanged); + break; + default: + throw new IllegalArgumentException("No high level state with ID " + stateID); + } + } + + @Override + public void removeListener(String stateID, java.util.function.Consumer stateChanged) + { + switch (stateID) + { + case "out": + hlsListeners.remove(stateChanged); + break; + default: + throw new IllegalArgumentException("No high level state with ID " + stateID); + } + } + @Override public String getIDForSerializing(IdentifyParams idParams) { @@ -102,7 +142,7 @@ public class ModelManualSwitch extends ModelComponent if (foreground != null) gc.setForeground(foreground); gc.drawRectangle(getBounds()); - String label = BitVectorFormatter.formatAsString(manualSwitch == null ? null : manualSwitch.getValues()); + String label = BitVectorFormatter.formatAsString(manualSwitch == null ? null : getOutValues()); Font oldFont = gc.getFont(); Font labelFont = new Font(oldFont.getName(), fontHeight, oldFont.getStyle()); gc.setFont(labelFont); @@ -120,7 +160,7 @@ public class ModelManualSwitch extends ModelComponent gc.drawLine(x, y + heightMiniButtons, x + width, y + heightMiniButtons); Color c = gc.getBackground(); gc.setBackground(gc.getForeground()); - BitVector bv = manualSwitch.getValues(); + BitVector bv = getOutValues(); double part = width / bv.length(); for (int i = 0; i < bv.length(); i++) { @@ -160,7 +200,7 @@ public class ModelManualSwitch extends ModelComponent if (heightMiniButtons > 0 && y - getPosY() < heightMiniButtons) { int part = (int) ((x - getPosX()) * logicWidth / width); - manualSwitch.setState(manualSwitch.getValues().withBitChanged(part, Bit::not)); + manualSwitch.setState(getOutValues().withBitChanged(part, Bit::not)); } else { manualSwitch.toggle(); @@ -191,6 +231,11 @@ public class ModelManualSwitch extends ModelComponent return logicWidth; } + private BitVector getOutValues() + { + return manualSwitch.getValues(); + } + static { LogicCoreAdapter.addComponentAdapter(new ManualSwitchAdapter()); diff --git a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/model/components/atomic/SimpleRectangularHardcodedModelComponent.java b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/model/components/atomic/SimpleRectangularHardcodedModelComponent.java index 8971285a..427a1792 100644 --- a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/model/components/atomic/SimpleRectangularHardcodedModelComponent.java +++ b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/model/components/atomic/SimpleRectangularHardcodedModelComponent.java @@ -1,7 +1,10 @@ package net.mograsim.logic.model.model.components.atomic; +import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; import net.haspamelodica.swt.helper.gcs.GeneralGC; import net.haspamelodica.swt.helper.swtobjectwrappers.Rectangle; @@ -20,6 +23,7 @@ import net.mograsim.logic.model.snippets.symbolrenderers.CenteredTextSymbolRende import net.mograsim.logic.model.snippets.symbolrenderers.PinNamesSymbolRenderer; import net.mograsim.logic.model.snippets.symbolrenderers.PinNamesSymbolRenderer.PinNamesParams; import net.mograsim.logic.model.snippets.symbolrenderers.PinNamesSymbolRenderer.PinNamesParams.Position; +import net.mograsim.logic.model.util.ObservableAtomicReference; public abstract class SimpleRectangularHardcodedModelComponent extends ModelComponent { @@ -33,9 +37,11 @@ public abstract class SimpleRectangularHardcodedModelComponent extends ModelComp private final CenteredTextSymbolRenderer centerTextRenderer; private final PinNamesSymbolRenderer pinNamesRenderer; - private AtomicReference state; + private ObservableAtomicReference state; private Runnable recalculate; + private final Map, Consumer>>> stateObsPerHLSListenerPerStateID; + // creation and destruction public SimpleRectangularHardcodedModelComponent(LogicModelModifiable model, String id, String name, String centerText) @@ -57,31 +63,47 @@ public abstract class SimpleRectangularHardcodedModelComponent extends ModelComp pinNamesParams.pinLabelMargin = pinNamesMargin; this.pinNamesRenderer = new PinNamesSymbolRenderer(this, pinNamesParams); addPinRemovedListener(this::pinRemoved); + + this.stateObsPerHLSListenerPerStateID = new HashMap<>(); + setHighLevelStateHandler(new HighLevelStateHandler() { + @Override - public String getIDForSerializing(IdentifyParams idParams) + public Object get(String stateID) { - return null;// we don't need to serialize this; it's implicit since we are a SimpleRectangularHardcodedModelComponent + return getHighLevelState(state.get(), stateID); } @Override - public Object getParamsForSerializing(IdentifyParams idParams) + public void set(String stateID, Object newState) { - return null; + state.updateAndGet(s -> SimpleRectangularHardcodedModelComponent.this.setHighLevelState(s, stateID, newState)); + recalculate.run(); } @Override - public Object getHighLevelState(String stateID) + public void addListener(String stateID, Consumer stateChanged) { - return SimpleRectangularHardcodedModelComponent.this.getHighLevelState(state.get(), stateID); + addHighLevelStateListener(state.get(), stateID, stateChanged); } @Override - public void setHighLevelState(String stateID, Object newState) + public void removeListener(String stateID, Consumer stateChanged) { - state.updateAndGet(s -> SimpleRectangularHardcodedModelComponent.this.setHighLevelState(s, stateID, newState)); - recalculate.run(); + removeHighLevelStateListener(state.get(), stateID, stateChanged); + } + + @Override + public String getIDForSerializing(IdentifyParams idParams) + { + return null;// we don't need to serialize this; it's implicit since we are a SimpleRectangularHardcodedModelComponent + } + + @Override + public Object getParamsForSerializing(IdentifyParams idParams) + { + return null; } }); @@ -116,13 +138,36 @@ public abstract class SimpleRectangularHardcodedModelComponent extends ModelComp throw new IllegalArgumentException("No high level state with ID " + stateID); } + protected void addHighLevelStateListener(Object state, String stateID, Consumer stateChanged) + { + AtomicReference lastHLSRef = new AtomicReference<>(getHighLevelState(state, stateID)); + Consumer> refObs = r -> + { + Object newHLS = getHighLevelState(stateID); + if (!Objects.equals(lastHLSRef.getAndSet(newHLS), newHLS)) + stateChanged.accept(newHLS); + }; + stateObsPerHLSListenerPerStateID.computeIfAbsent(stateID, s -> new HashMap<>()).put(stateChanged, refObs); + this.state.addObserver(refObs); + } + + protected void removeHighLevelStateListener(Object state, String stateID, Consumer stateChanged) + { + getHighLevelState(state, stateID);// if this throws, we know there is no HLS with this name + var stateObsPerHLSListener = stateObsPerHLSListenerPerStateID.get(stateID); + if (stateObsPerHLSListener == null) + return; + Consumer> refObs = stateObsPerHLSListener.remove(stateChanged); + this.state.removeObserver(refObs); + } + // logic public abstract Object recalculate(Object lastState, Map readEnds, Map readWriteEnds); // core model binding - public void setCoreModelBindingAndResetState(AtomicReference state, Runnable recalculate) + public void setCoreModelBindingAndResetState(ObservableAtomicReference state, Runnable recalculate) { this.state = state; this.recalculate = recalculate; diff --git a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/model/wires/ModelWire.java b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/model/wires/ModelWire.java index 1f44f570..8d3cbded 100644 --- a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/model/wires/ModelWire.java +++ b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/model/wires/ModelWire.java @@ -513,6 +513,28 @@ public class ModelWire return end.getValues(); } + /** + * Registers the given {@link LogicObserver} for the {@link ReadEnd} this {@link ModelWire} is bound to. + * + * @see ReadEnd#registerObserver(LogicObserver) + * @author Daniel Kirschten + */ + public void addObserver(LogicObserver obs) + { + end.registerObserver(obs); + } + + /** + * Deregisters the given {@link LogicObserver} for the {@link ReadEnd} this {@link ModelWire} is bound to. + * + * @see ReadEnd#deregisterObserver(LogicObserver) + * @author Daniel Kirschten + */ + public void removeObserver(LogicObserver obs) + { + end.deregisterObserver(obs); + } + // listeners // @formatter:off diff --git a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/modeladapter/componentadapters/SimpleRectangularHardcodedModelComponentAdapter.java b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/modeladapter/componentadapters/SimpleRectangularHardcodedModelComponentAdapter.java index dbebc0ff..e4bd59dc 100644 --- a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/modeladapter/componentadapters/SimpleRectangularHardcodedModelComponentAdapter.java +++ b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/modeladapter/componentadapters/SimpleRectangularHardcodedModelComponentAdapter.java @@ -2,7 +2,6 @@ package net.mograsim.logic.model.modeladapter.componentadapters; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.atomic.AtomicReference; import net.mograsim.logic.core.LogicObserver; import net.mograsim.logic.core.timeline.Timeline; @@ -13,6 +12,7 @@ import net.mograsim.logic.model.model.components.atomic.SimpleRectangularHardcod import net.mograsim.logic.model.model.wires.Pin; import net.mograsim.logic.model.model.wires.PinUsage; import net.mograsim.logic.model.modeladapter.CoreModelParameters; +import net.mograsim.logic.model.util.ObservableAtomicReference; public class SimpleRectangularHardcodedModelComponentAdapter implements ComponentAdapter { @@ -29,7 +29,7 @@ public class SimpleRectangularHardcodedModelComponentAdapter implements Componen Map readEnds = new HashMap<>(); Map readWriteEnds = new HashMap<>(); - AtomicReference state = new AtomicReference<>(); + ObservableAtomicReference state = new ObservableAtomicReference<>(); Runnable recalculate = () -> state.updateAndGet(s -> modelComponent.recalculate(s, readEnds, readWriteEnds)); LogicObserver logicObs = c -> timeline.addEvent(e -> recalculate.run(), params.gateProcessTime); diff --git a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/HighLevelStateHandler.java b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/HighLevelStateHandler.java index 622df00b..78590924 100644 --- a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/HighLevelStateHandler.java +++ b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/HighLevelStateHandler.java @@ -1,5 +1,7 @@ package net.mograsim.logic.model.snippets; +import java.util.function.Consumer; + import net.mograsim.logic.model.model.components.ModelComponent; import net.mograsim.logic.model.serializing.JSONSerializable; @@ -20,22 +22,25 @@ public interface HighLevelStateHandler extends JSONSerializable * Gets the current value of the given high-level state.
* See {@link HighLevelStateHandler} for an explanation of high-level state IDs. * - * @see #setHighLevelState(String, Object) + * @see #set(String, Object) * @see ModelComponent#getHighLevelState(String) * * @author Daniel Kirschten */ - public Object getHighLevelState(String stateID); + public Object get(String stateID); /** * Sets the given high-level state to the given value.
* See {@link HighLevelStateHandler} for an explanation of high-level state IDs. * - * @see #getHighLevelState(String) + * @see #get(String) * @see ModelComponent#setHighLevelState(String, Object) * * @author Daniel Kirschten */ - public void setHighLevelState(String stateID, Object newState); + public void set(String stateID, Object newState); + + public void addListener(String stateID, Consumer stateChanged); + public void removeListener(String stateID, Consumer stateChanged); } \ No newline at end of file diff --git a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/DefaultHighLevelStateHandler.java b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/DefaultHighLevelStateHandler.java index 7fd1fc96..34b27a98 100644 --- a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/DefaultHighLevelStateHandler.java +++ b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/DefaultHighLevelStateHandler.java @@ -1,5 +1,7 @@ package net.mograsim.logic.model.snippets.highlevelstatehandlers; +import java.util.function.Consumer; + import net.mograsim.logic.model.model.components.submodels.SubmodelComponent; import net.mograsim.logic.model.serializing.IdentifyParams; import net.mograsim.logic.model.snippets.HighLevelStateHandler; @@ -25,13 +27,25 @@ public class DefaultHighLevelStateHandler implements HighLevelStateHandler } @Override - public Object getHighLevelState(String stateID) + public Object get(String stateID) + { + throw new IllegalArgumentException("No high level state with ID " + stateID); + } + + @Override + public void set(String stateID, Object newState) + { + throw new IllegalArgumentException("No high level state with ID " + stateID); + } + + @Override + public void addListener(String stateID, Consumer stateChanged) { throw new IllegalArgumentException("No high level state with ID " + stateID); } @Override - public void setHighLevelState(String stateID, Object newState) + public void removeListener(String stateID, Consumer stateChanged) { throw new IllegalArgumentException("No high level state with ID " + stateID); } diff --git a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/standard/StandardHighLevelStateHandler.java b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/standard/StandardHighLevelStateHandler.java index 58e8e64c..ddb52ae1 100644 --- a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/standard/StandardHighLevelStateHandler.java +++ b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/standard/StandardHighLevelStateHandler.java @@ -6,6 +6,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; import java.util.function.BiFunction; +import java.util.function.Consumer; import java.util.function.Function; import net.mograsim.logic.model.model.components.submodels.SubmodelComponent; @@ -129,7 +130,7 @@ public class StandardHighLevelStateHandler implements HighLevelStateHandler } @Override - public Object getHighLevelState(String stateID) + public Object get(String stateID) { int indexOfDot = stateID.indexOf('.'); if (indexOfDot == -1) @@ -147,7 +148,7 @@ public class StandardHighLevelStateHandler implements HighLevelStateHandler } @Override - public void setHighLevelState(String stateID, Object newState) + public void set(String stateID, Object newState) { int indexOfDot = stateID.indexOf('.'); if (indexOfDot == -1) @@ -167,6 +168,48 @@ public class StandardHighLevelStateHandler implements HighLevelStateHandler } } + @Override + public void addListener(String stateID, Consumer stateChanged) + { + int indexOfDot = stateID.indexOf('.'); + if (indexOfDot == -1) + { + AtomicHighLevelStateHandler handler = atomicHighLevelStateHandlers.get(stateID); + if (handler != null) + handler.addListener(stateChanged); + else + throw new IllegalArgumentException("No high level state with ID " + stateID); + } else + { + SubcomponentHighLevelStateHandler handler = subcomponentHighLevelStateHandlers.get(stateID.substring(0, indexOfDot)); + if (handler != null) + handler.addListener(stateID.substring(indexOfDot + 1), stateChanged); + else + throw new IllegalArgumentException("No high level state with ID " + stateID); + } + } + + @Override + public void removeListener(String stateID, Consumer stateChanged) + { + int indexOfDot = stateID.indexOf('.'); + if (indexOfDot == -1) + { + AtomicHighLevelStateHandler handler = atomicHighLevelStateHandlers.get(stateID); + if (handler != null) + handler.removeListener(stateChanged); + else + throw new IllegalArgumentException("No high level state with ID " + stateID); + } else + { + SubcomponentHighLevelStateHandler handler = subcomponentHighLevelStateHandlers.get(stateID.substring(0, indexOfDot)); + if (handler != null) + handler.removeListener(stateID.substring(indexOfDot + 1), stateChanged); + else + throw new IllegalArgumentException("No high level state with ID " + stateID); + } + } + @Override public String getIDForSerializing(IdentifyParams idParams) { diff --git a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/standard/atomic/AtomicHighLevelStateHandler.java b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/standard/atomic/AtomicHighLevelStateHandler.java index 97ace092..cbcd95ec 100644 --- a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/standard/atomic/AtomicHighLevelStateHandler.java +++ b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/standard/atomic/AtomicHighLevelStateHandler.java @@ -1,5 +1,7 @@ package net.mograsim.logic.model.snippets.highlevelstatehandlers.standard.atomic; +import java.util.function.Consumer; + import com.google.gson.JsonElement; import net.mograsim.logic.model.serializing.JSONSerializable; @@ -28,4 +30,8 @@ public interface AtomicHighLevelStateHandler extends JSONSerializable public String id; public JsonElement params; } + + public void addListener(Consumer stateChanged); + + public void removeListener(Consumer stateChanged); } \ No newline at end of file diff --git a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/standard/atomic/BitVectorSplittingAtomicHighLevelStateHandler.java b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/standard/atomic/BitVectorSplittingAtomicHighLevelStateHandler.java index 7ae29128..4289808d 100644 --- a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/standard/atomic/BitVectorSplittingAtomicHighLevelStateHandler.java +++ b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/standard/atomic/BitVectorSplittingAtomicHighLevelStateHandler.java @@ -2,7 +2,10 @@ package net.mograsim.logic.model.snippets.highlevelstatehandlers.standard.atomic import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.function.Consumer; import net.mograsim.logic.core.types.Bit; import net.mograsim.logic.core.types.BitVector; @@ -20,6 +23,8 @@ public class BitVectorSplittingAtomicHighLevelStateHandler implements AtomicHigh private final List vectorPartLengthesUnmodifiable; private int length; + private final Map, Consumer> targetListeners; + public BitVectorSplittingAtomicHighLevelStateHandler(SubmodelComponent component) { this(component, null); @@ -33,6 +38,9 @@ public class BitVectorSplittingAtomicHighLevelStateHandler implements AtomicHigh this.vectorPartTargetsUnmodifiable = Collections.unmodifiableList(vectorPartTargets); this.vectorPartLengthes = new ArrayList<>(); this.vectorPartLengthesUnmodifiable = Collections.unmodifiableList(vectorPartLengthes); + + this.targetListeners = new HashMap<>(); + if (params != null) setVectorParts(params.vectorPartTargets, params.vectorPartLengthes); } @@ -111,6 +119,32 @@ public class BitVectorSplittingAtomicHighLevelStateHandler implements AtomicHigh } } + @Override + public void addListener(Consumer stateChanged) + { + if (targetListeners.get(stateChanged) != null) + // this listener is/was already registered + return; + + Consumer targetListener = o -> stateChanged.accept(getHighLevelState()); + targetListeners.put(stateChanged, targetListener); + + for (String target : vectorPartTargets) + component.addHighLevelStateListener(target, targetListener); + } + + @Override + public void removeListener(Consumer stateChanged) + { + Consumer targetListener = targetListeners.get(stateChanged); + if (targetListener == null) + // this listener is/was not registered + return; + + for (String target : vectorPartTargets) + component.removeHighLevelStateListener(target, targetListener); + } + @Override public String getIDForSerializing(IdentifyParams idParams) { diff --git a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/standard/atomic/DelegatingAtomicHighLevelStateHandler.java b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/standard/atomic/DelegatingAtomicHighLevelStateHandler.java index cdc7e4a2..98890fe7 100644 --- a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/standard/atomic/DelegatingAtomicHighLevelStateHandler.java +++ b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/standard/atomic/DelegatingAtomicHighLevelStateHandler.java @@ -1,5 +1,7 @@ package net.mograsim.logic.model.snippets.highlevelstatehandlers.standard.atomic; +import java.util.function.Consumer; + import net.mograsim.logic.model.model.components.ModelComponent; import net.mograsim.logic.model.model.components.submodels.SubmodelComponent; import net.mograsim.logic.model.serializing.IdentifyParams; @@ -75,17 +77,35 @@ public class DelegatingAtomicHighLevelStateHandler implements AtomicHighLevelSta @Override public Object getHighLevelState() { - if (delegateTarget == null) - throw new IllegalStateException("Delegating to a component that was destroyed"); + checkTarget(); return delegateTarget.getHighLevelState(subStateID); } @Override public void setHighLevelState(Object newState) + { + checkTarget(); + delegateTarget.setHighLevelState(subStateID, newState); + } + + @Override + public void addListener(Consumer stateChanged) + { + checkTarget(); + delegateTarget.addHighLevelStateListener(subStateID, stateChanged); + } + + @Override + public void removeListener(Consumer stateChanged) + { + checkTarget(); + delegateTarget.removeHighLevelStateListener(subStateID, stateChanged); + } + + private void checkTarget() { if (delegateTarget == null) throw new IllegalStateException("Delegating to a component that was destroyed"); - delegateTarget.setHighLevelState(subStateID, newState); } @Override @@ -97,8 +117,7 @@ public class DelegatingAtomicHighLevelStateHandler implements AtomicHighLevelSta @Override public DelegatingAtomicHighLevelStateHandlerParams getParamsForSerializing(IdentifyParams idParams) { - if (delegateTarget == null) - throw new IllegalStateException("Delegating to a component that was destroyed"); + checkTarget(); DelegatingAtomicHighLevelStateHandlerParams params = new DelegatingAtomicHighLevelStateHandlerParams(); params.delegateTarget = delegateTarget == parentComponent ? null : delegateTarget.getName(); params.subStateID = subStateID; diff --git a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/standard/atomic/WireForcingAtomicHighLevelStateHandler.java b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/standard/atomic/WireForcingAtomicHighLevelStateHandler.java index aa509c9d..4f7cd457 100644 --- a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/standard/atomic/WireForcingAtomicHighLevelStateHandler.java +++ b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/standard/atomic/WireForcingAtomicHighLevelStateHandler.java @@ -2,11 +2,16 @@ package net.mograsim.logic.model.snippets.highlevelstatehandlers.standard.atomic import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; +import net.mograsim.logic.core.LogicObserver; import net.mograsim.logic.core.types.Bit; import net.mograsim.logic.core.types.BitVector; import net.mograsim.logic.model.model.components.submodels.SubmodelComponent; @@ -24,6 +29,8 @@ public class WireForcingAtomicHighLevelStateHandler implements AtomicHighLevelSt private final List wiresToForceInverted; private final List wiresToForceInvertedUnmodifiable; + private final Map, LogicObserver> wireObsPerListener; + public WireForcingAtomicHighLevelStateHandler(SubmodelComponent component) { this(component, null); @@ -36,6 +43,9 @@ public class WireForcingAtomicHighLevelStateHandler implements AtomicHighLevelSt this.wiresToForceUnmodifiable = Collections.unmodifiableList(wiresToForce); this.wiresToForceInverted = new ArrayList<>(); this.wiresToForceInvertedUnmodifiable = Collections.unmodifiableList(wiresToForceInverted); + + this.wireObsPerListener = new HashMap<>(); + if (params != null) { Map wiresByName = component.submodel.getWiresByName(); @@ -133,6 +143,37 @@ public class WireForcingAtomicHighLevelStateHandler implements AtomicHighLevelSt wire.forceWireValues(vector); } + @Override + public void addListener(Consumer stateChanged) + { + if (wireObsPerListener.containsKey(stateChanged)) + return; + AtomicReference lastStateRef = new AtomicReference<>(getHighLevelState()); + LogicObserver obs = w -> + { + Object newState = getHighLevelState(); + if (!Objects.equals(lastStateRef.getAndSet(newState), newState)) + stateChanged.accept(newState); + }; + wireObsPerListener.put(stateChanged, obs); + for (ModelWire w : wiresToForce) + w.addObserver(obs); + for (ModelWire w : wiresToForceInverted) + w.addObserver(obs); + } + + @Override + public void removeListener(Consumer stateChanged) + { + LogicObserver obs = wireObsPerListener.remove(stateChanged); + if (obs == null) + return; + for (ModelWire w : wiresToForce) + w.removeObserver(obs); + for (ModelWire w : wiresToForceInverted) + w.removeObserver(obs); + } + @Override public String getIDForSerializing(IdentifyParams idParams) { diff --git a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/standard/subcomponent/DelegatingSubcomponentHighLevelStateHandler.java b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/standard/subcomponent/DelegatingSubcomponentHighLevelStateHandler.java index 104cb142..6557787f 100644 --- a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/standard/subcomponent/DelegatingSubcomponentHighLevelStateHandler.java +++ b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/standard/subcomponent/DelegatingSubcomponentHighLevelStateHandler.java @@ -1,5 +1,7 @@ package net.mograsim.logic.model.snippets.highlevelstatehandlers.standard.subcomponent; +import java.util.function.Consumer; + import net.mograsim.logic.model.model.components.ModelComponent; import net.mograsim.logic.model.model.components.submodels.SubmodelComponent; import net.mograsim.logic.model.serializing.IdentifyParams; @@ -76,17 +78,35 @@ public class DelegatingSubcomponentHighLevelStateHandler implements Subcomponent @Override public Object getHighLevelState(String subStateID) { - if (delegateTarget == null) - throw new IllegalStateException("Delegating to a component that was destroyed"); + checkTarget(); return delegateTarget.getHighLevelState(getDelegateTargetHighLevelStateID(subStateID)); } @Override public void setHighLevelState(String subStateID, Object newState) + { + checkTarget(); + delegateTarget.setHighLevelState(getDelegateTargetHighLevelStateID(subStateID), newState); + } + + @Override + public void addListener(String subStateID, Consumer stateChanged) + { + checkTarget(); + delegateTarget.addHighLevelStateListener(getDelegateTargetHighLevelStateID(subStateID), stateChanged); + } + + @Override + public void removeListener(String subStateID, Consumer stateChanged) + { + checkTarget(); + delegateTarget.removeHighLevelStateListener(getDelegateTargetHighLevelStateID(subStateID), stateChanged); + } + + private void checkTarget() { if (delegateTarget == null) throw new IllegalStateException("Delegating to a component that was destroyed"); - delegateTarget.setHighLevelState(getDelegateTargetHighLevelStateID(subStateID), newState); } private String getDelegateTargetHighLevelStateID(String subStateID) @@ -103,8 +123,7 @@ public class DelegatingSubcomponentHighLevelStateHandler implements Subcomponent @Override public DelegatingSubcomponentHighLevelStateHandlerParams getParamsForSerializing(IdentifyParams idParams) { - if (delegateTarget == null) - throw new IllegalStateException("Delegating to a component that was destroyed"); + checkTarget(); DelegatingSubcomponentHighLevelStateHandlerParams params = new DelegatingSubcomponentHighLevelStateHandlerParams(); params.delegateTarget = delegateTarget == parentComponent ? null : delegateTarget.getName(); params.prefix = prefix; diff --git a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/standard/subcomponent/SubcomponentHighLevelStateHandler.java b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/standard/subcomponent/SubcomponentHighLevelStateHandler.java index 4c82bdcf..fdc633c3 100644 --- a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/standard/subcomponent/SubcomponentHighLevelStateHandler.java +++ b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/snippets/highlevelstatehandlers/standard/subcomponent/SubcomponentHighLevelStateHandler.java @@ -1,5 +1,7 @@ package net.mograsim.logic.model.snippets.highlevelstatehandlers.standard.subcomponent; +import java.util.function.Consumer; + import com.google.gson.JsonElement; import net.mograsim.logic.model.serializing.JSONSerializable; @@ -28,4 +30,8 @@ public interface SubcomponentHighLevelStateHandler extends JSONSerializable public String id; public JsonElement params; } + + public void addListener(String subStateID, Consumer stateChanged); + + public void removeListener(String subStateID, Consumer stateChanged); } \ No newline at end of file diff --git a/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/util/ObservableAtomicReference.java b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/util/ObservableAtomicReference.java new file mode 100644 index 00000000..0e180f10 --- /dev/null +++ b/plugins/net.mograsim.logic.model/src/net/mograsim/logic/model/util/ObservableAtomicReference.java @@ -0,0 +1,92 @@ +package net.mograsim.logic.model.util; + +import java.lang.invoke.VarHandle; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import java.util.function.UnaryOperator; + +public class ObservableAtomicReference +{ + private final AtomicReference ref; + + private final List>> observers; + + public ObservableAtomicReference() + { + ref = new AtomicReference<>(); + observers = new ArrayList<>(); + } + + public ObservableAtomicReference(V initialValue) + { + ref = new AtomicReference<>(initialValue); + observers = new ArrayList<>(); + } + + /** + * Returns the current value, with memory effects as specified by {@link VarHandle#getVolatile}. + * + * @return the current value + */ + public V get() + { + return ref.get(); + } + + /** + * Sets the value to {@code newValue}, with memory effects as specified by {@link VarHandle#setVolatile}. + * + * @param newValue the new value + */ + public void set(V newValue) + { + ref.set(newValue); + callObservers(); + } + + /** + * Atomically sets the value to {@code newValue} and returns the old value, with memory effects as specified by + * {@link VarHandle#getAndSet}. + * + * @param newValue the new value + * @return the previous value + */ + public V getAndSet(V newValue) + { + V oldValue = ref.getAndSet(newValue); + callObservers(); + return oldValue; + } + + /** + * Atomically updates (with memory effects as specified by {@link VarHandle#compareAndSet}) the current value with the results of + * applying the given function, returning the updated value. The function should be side-effect-free, since it may be re-applied when + * attempted updates fail due to contention among threads. + * + * @param updateFunction a side-effect-free function + * @return the updated value + */ + public V updateAndGet(UnaryOperator updateFunction) + { + V updatedValue = ref.updateAndGet(updateFunction); + callObservers(); + return updatedValue; + } + + public void addObserver(Consumer> obs) + { + observers.add(obs); + } + + public void removeObserver(Consumer> obs) + { + observers.add(obs); + } + + private void callObservers() + { + observers.forEach(o -> o.accept(this)); + } +} \ No newline at end of file diff --git a/plugins/net.mograsim.machine/src/net/mograsim/machine/Machine.java b/plugins/net.mograsim.machine/src/net/mograsim/machine/Machine.java index 8325068d..74328438 100644 --- a/plugins/net.mograsim.machine/src/net/mograsim/machine/Machine.java +++ b/plugins/net.mograsim.machine/src/net/mograsim/machine/Machine.java @@ -15,6 +15,7 @@ public interface Machine LogicModel getModel(); + // TODO replace with HLS references CoreClock getClock(); BitVector getRegister(Register r); diff --git a/plugins/net.mograsim.machine/src/net/mograsim/machine/mi/components/ModelMicroInstructionMemory.java b/plugins/net.mograsim.machine/src/net/mograsim/machine/mi/components/ModelMicroInstructionMemory.java index 706734f2..25c0b68b 100644 --- a/plugins/net.mograsim.machine/src/net/mograsim/machine/mi/components/ModelMicroInstructionMemory.java +++ b/plugins/net.mograsim.machine/src/net/mograsim/machine/mi/components/ModelMicroInstructionMemory.java @@ -1,5 +1,9 @@ package net.mograsim.machine.mi.components; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + import net.mograsim.logic.model.model.LogicModelModifiable; import net.mograsim.logic.model.model.wires.Pin; import net.mograsim.logic.model.model.wires.PinUsage; @@ -16,6 +20,8 @@ public abstract class ModelMicroInstructionMemory extends ModelMemory private CoreMicroInstructionMemory memory; private final MicroInstructionMemoryDefinition definition; + private final List> memoryBindingListeners; + public ModelMicroInstructionMemory(LogicModelModifiable model, MicroInstructionMemoryDefinition definition, String name) { super(model, 120, 150, name, "MPM", false); @@ -24,10 +30,12 @@ public abstract class ModelMicroInstructionMemory extends ModelMemory addPin(dataPin = new Pin(model, this, "D", definition.getMicroInstructionDefinition().sizeInBits(), PinUsage.OUTPUT, getWidth(), 50)); + memoryBindingListeners = new ArrayList<>(); + setHighLevelStateHandler(new HighLevelStateHandler() { @Override - public Object getHighLevelState(String stateID) + public Object get(String stateID) { if (stateID.equals("memory_binding")) return memory.getMemory(); @@ -35,10 +43,30 @@ public abstract class ModelMicroInstructionMemory extends ModelMemory } @Override - public void setHighLevelState(String stateID, Object newState) + public void set(String stateID, Object newState) { if (stateID.equals("memory_binding")) + { memory.setMemory((MicroInstructionMemory) newState); + memoryBindingListeners.forEach(l -> l.accept(newState)); + } else + throw new IllegalArgumentException("No high level state with ID " + stateID); + } + + @Override + public void addListener(String stateID, Consumer stateChanged) + { + if (stateID.equals("memory_binding")) + memoryBindingListeners.add(stateChanged); + else + throw new IllegalArgumentException("No high level state with ID " + stateID); + } + + @Override + public void removeListener(String stateID, Consumer stateChanged) + { + if (stateID.equals("memory_binding")) + memoryBindingListeners.remove(stateChanged); else throw new IllegalArgumentException("No high level state with ID " + stateID); } diff --git a/plugins/net.mograsim.machine/src/net/mograsim/machine/standard/memory/ModelWordAddressableMemory.java b/plugins/net.mograsim.machine/src/net/mograsim/machine/standard/memory/ModelWordAddressableMemory.java index b2498498..a1112e68 100644 --- a/plugins/net.mograsim.machine/src/net/mograsim/machine/standard/memory/ModelWordAddressableMemory.java +++ b/plugins/net.mograsim.machine/src/net/mograsim/machine/standard/memory/ModelWordAddressableMemory.java @@ -1,5 +1,9 @@ package net.mograsim.machine.standard.memory; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + import net.mograsim.logic.model.model.LogicModelModifiable; import net.mograsim.logic.model.model.wires.Pin; import net.mograsim.logic.model.model.wires.PinUsage; @@ -16,6 +20,8 @@ public abstract class ModelWordAddressableMemory extends ModelMemory private CoreWordAddressableMemory memory; private MainMemoryDefinition definition; + private final List> memoryBindingListeners; + public ModelWordAddressableMemory(LogicModelModifiable model, MainMemoryDefinition definition, String name) { super(model, 120, 150, name, "RAM", false); @@ -25,10 +31,12 @@ public abstract class ModelWordAddressableMemory extends ModelMemory addPin(dataPin = new Pin(model, this, "D", definition.getCellWidth(), PinUsage.TRISTATE, getWidth(), 50)); addPin(rWPin = new Pin(model, this, "RW", 1, PinUsage.INPUT, getWidth(), 70)); + memoryBindingListeners = new ArrayList<>(); + setHighLevelStateHandler(new HighLevelStateHandler() { @Override - public Object getHighLevelState(String stateID) + public Object get(String stateID) { if (stateID.equals("memory_binding")) return memory.getMemory(); @@ -36,10 +44,30 @@ public abstract class ModelWordAddressableMemory extends ModelMemory } @Override - public void setHighLevelState(String stateID, Object newState) + public void set(String stateID, Object newState) { if (stateID.equals("memory_binding")) + { memory.setMemory((MainMemory) newState); + memoryBindingListeners.forEach(l -> l.accept(newState)); + } else + throw new IllegalArgumentException("No high level state with ID " + stateID); + } + + @Override + public void addListener(String stateID, Consumer stateChanged) + { + if (stateID.equals("memory_binding")) + memoryBindingListeners.add(stateChanged); + else + throw new IllegalArgumentException("No high level state with ID " + stateID); + } + + @Override + public void removeListener(String stateID, Consumer stateChanged) + { + if (stateID.equals("memory_binding")) + memoryBindingListeners.remove(stateChanged); else throw new IllegalArgumentException("No high level state with ID " + stateID); } -- 2.17.1