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;
List<ModelComponent> componentsByItemIndex = new ArrayList<>();
List<LogicModel> models = new ArrayList<>();
AtomicBoolean recalculateQueued = new AtomicBoolean();
- AtomicReference<Consumer<? super ModelComponent>> compAdded = new AtomicReference<>();
- AtomicReference<Consumer<? super ModelComponent>> 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<? super ModelComponent>[] compAdded = new Consumer[1];
+ @SuppressWarnings("unchecked")
+ Consumer<? super ModelComponent>[] 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: ");
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 ->
valueText.addListener(SWT.DefaultSelection, sendAction);
get.addListener(SWT.Selection, getAction);
stateIDText.addListener(SWT.DefaultSelection, getAction);
+ Map<ModelComponent, Map<String, Consumer<Object>>> 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<String, Consumer<Object>> sysoutListenersPerHLS = sysoutListenersPerHLSPerTarget.computeIfAbsent(target,
+ k -> new HashMap<>());
+ String stateIDString = stateIDText.getText();
+ if (sysoutListenersPerHLS.containsKey(stateIDString))
+ throw new RuntimeException("Listener already registered");
+ Consumer<Object> 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<String, Consumer<Object>> sysoutListenersPerHLS = sysoutListenersPerHLSPerTarget.get(target);
+ if (sysoutListenersPerHLS == null)
+ throw new RuntimeException("Listener not registered");
+ String stateIDString = stateIDText.getText();
+ Consumer<Object> 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());
}
* 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);
}
/**
* 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<Object> stateChanged)
+ {
+ highLevelStateHandler.addListener(stateID, stateChanged);
+ }
+
+ public final void removeHighLevelStateListener(String stateID, Consumer<Object> stateChanged)
+ {
+ highLevelStateHandler.removeListener(stateID, stateChanged);
}
// "graphical" operations
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;
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;
private OrientationCalculator oc;
private CoreClock clock;
+ private final List<Consumer<Object>> hlsListeners;
+
public ModelClock(LogicModelModifiable model, ModelClockParams params)
{
this(model, params, null);
{
super(model, name, false);
this.params = params;
- logicObs = (i) -> model.requestRedraw();
oc = new OrientationCalculator(params.orientation, width, height);
setSize(oc.width(), oc.height());
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);
}
@Override
- public void setHighLevelState(String stateID, Object newState)
+ public void set(String stateID, Object newState)
{
switch (stateID)
{
}
}
+ @Override
+ public void addListener(String stateID, Consumer<Object> 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<Object> 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)
{
return clock != null;
}
+ // TODO remove
public CoreClock getClock()
{
return clock;
return params;
}
+ private BitVector getOutValues()
+ {
+ return clock.getOutValues();
+ }
+
static
{
LogicCoreAdapter.addComponentAdapter(new ClockAdapter());
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;
private final LogicObserver logicObs;
private CoreManualSwitch manualSwitch;
+ private final List<Consumer<Object>> hlsListeners;
+
public ModelManualSwitch(LogicModelModifiable model, int logicWidth)
{
this(model, logicWidth, null);
{
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);
}
@Override
- public void setHighLevelState(String stateID, Object newState)
+ public void set(String stateID, Object newState)
{
switch (stateID)
{
}
}
+ @Override
+ public void addListener(String stateID, Consumer<Object> 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<Object> 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)
{
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);
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++)
{
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();
return logicWidth;
}
+ private BitVector getOutValues()
+ {
+ return manualSwitch.getValues();
+ }
+
static
{
LogicCoreAdapter.addComponentAdapter(new ManualSwitchAdapter());
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;
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
{
private final CenteredTextSymbolRenderer centerTextRenderer;
private final PinNamesSymbolRenderer pinNamesRenderer;
- private AtomicReference<Object> state;
+ private ObservableAtomicReference<Object> state;
private Runnable recalculate;
+ private final Map<String, Map<Consumer<Object>, Consumer<ObservableAtomicReference<Object>>>> stateObsPerHLSListenerPerStateID;
+
// creation and destruction
public SimpleRectangularHardcodedModelComponent(LogicModelModifiable model, String id, String name, String centerText)
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<Object> 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<Object> 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;
}
});
throw new IllegalArgumentException("No high level state with ID " + stateID);
}
+ protected void addHighLevelStateListener(Object state, String stateID, Consumer<Object> stateChanged)
+ {
+ AtomicReference<Object> lastHLSRef = new AtomicReference<>(getHighLevelState(state, stateID));
+ Consumer<ObservableAtomicReference<Object>> 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<Object> 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<ObservableAtomicReference<Object>> refObs = stateObsPerHLSListener.remove(stateChanged);
+ this.state.removeObserver(refObs);
+ }
+
// logic
public abstract Object recalculate(Object lastState, Map<String, ReadEnd> readEnds, Map<String, ReadWriteEnd> readWriteEnds);
// core model binding
- public void setCoreModelBindingAndResetState(AtomicReference<Object> state, Runnable recalculate)
+ public void setCoreModelBindingAndResetState(ObservableAtomicReference<Object> state, Runnable recalculate)
{
this.state = state;
this.recalculate = recalculate;
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
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;
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<SimpleRectangularHardcodedModelComponent>
{
Map<String, ReadEnd> readEnds = new HashMap<>();
Map<String, ReadWriteEnd> readWriteEnds = new HashMap<>();
- AtomicReference<Object> state = new AtomicReference<>();
+ ObservableAtomicReference<Object> state = new ObservableAtomicReference<>();
Runnable recalculate = () -> state.updateAndGet(s -> modelComponent.recalculate(s, readEnds, readWriteEnds));
LogicObserver logicObs = c -> timeline.addEvent(e -> recalculate.run(), params.gateProcessTime);
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;
* Gets the current value of the given high-level state. <br>
* 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. <br>
* 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<Object> stateChanged);
+ public void removeListener(String stateID, Consumer<Object> stateChanged);
}
\ No newline at end of file
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;
}
@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<Object> 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<Object> stateChanged)
{
throw new IllegalArgumentException("No high level state with ID " + stateID);
}
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;
}
@Override
- public Object getHighLevelState(String stateID)
+ public Object get(String stateID)
{
int indexOfDot = stateID.indexOf('.');
if (indexOfDot == -1)
}
@Override
- public void setHighLevelState(String stateID, Object newState)
+ public void set(String stateID, Object newState)
{
int indexOfDot = stateID.indexOf('.');
if (indexOfDot == -1)
}
}
+ @Override
+ public void addListener(String stateID, Consumer<Object> 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<Object> 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)
{
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;
public String id;
public JsonElement params;
}
+
+ public void addListener(Consumer<Object> stateChanged);
+
+ public void removeListener(Consumer<Object> stateChanged);
}
\ No newline at end of file
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;
private final List<Integer> vectorPartLengthesUnmodifiable;
private int length;
+ private final Map<Consumer<Object>, Consumer<Object>> targetListeners;
+
public BitVectorSplittingAtomicHighLevelStateHandler(SubmodelComponent component)
{
this(component, null);
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);
}
}
}
+ @Override
+ public void addListener(Consumer<Object> stateChanged)
+ {
+ if (targetListeners.get(stateChanged) != null)
+ // this listener is/was already registered
+ return;
+
+ Consumer<Object> targetListener = o -> stateChanged.accept(getHighLevelState());
+ targetListeners.put(stateChanged, targetListener);
+
+ for (String target : vectorPartTargets)
+ component.addHighLevelStateListener(target, targetListener);
+ }
+
+ @Override
+ public void removeListener(Consumer<Object> stateChanged)
+ {
+ Consumer<Object> 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)
{
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;
@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<Object> stateChanged)
+ {
+ checkTarget();
+ delegateTarget.addHighLevelStateListener(subStateID, stateChanged);
+ }
+
+ @Override
+ public void removeListener(Consumer<Object> 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
@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;
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;
private final List<ModelWire> wiresToForceInverted;
private final List<ModelWire> wiresToForceInvertedUnmodifiable;
+ private final Map<Consumer<Object>, LogicObserver> wireObsPerListener;
+
public WireForcingAtomicHighLevelStateHandler(SubmodelComponent component)
{
this(component, null);
this.wiresToForceUnmodifiable = Collections.unmodifiableList(wiresToForce);
this.wiresToForceInverted = new ArrayList<>();
this.wiresToForceInvertedUnmodifiable = Collections.unmodifiableList(wiresToForceInverted);
+
+ this.wireObsPerListener = new HashMap<>();
+
if (params != null)
{
Map<String, ModelWire> wiresByName = component.submodel.getWiresByName();
wire.forceWireValues(vector);
}
+ @Override
+ public void addListener(Consumer<Object> stateChanged)
+ {
+ if (wireObsPerListener.containsKey(stateChanged))
+ return;
+ AtomicReference<Object> 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<Object> 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)
{
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;
@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<Object> stateChanged)
+ {
+ checkTarget();
+ delegateTarget.addHighLevelStateListener(getDelegateTargetHighLevelStateID(subStateID), stateChanged);
+ }
+
+ @Override
+ public void removeListener(String subStateID, Consumer<Object> 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)
@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;
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;
public String id;
public JsonElement params;
}
+
+ public void addListener(String subStateID, Consumer<Object> stateChanged);
+
+ public void removeListener(String subStateID, Consumer<Object> stateChanged);
}
\ No newline at end of file
--- /dev/null
+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<V>
+{
+ private final AtomicReference<V> ref;
+
+ private final List<Consumer<ObservableAtomicReference<V>>> 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<V> updateFunction)
+ {
+ V updatedValue = ref.updateAndGet(updateFunction);
+ callObservers();
+ return updatedValue;
+ }
+
+ public void addObserver(Consumer<ObservableAtomicReference<V>> obs)
+ {
+ observers.add(obs);
+ }
+
+ public void removeObserver(Consumer<ObservableAtomicReference<V>> obs)
+ {
+ observers.add(obs);
+ }
+
+ private void callObservers()
+ {
+ observers.forEach(o -> o.accept(this));
+ }
+}
\ No newline at end of file
LogicModel getModel();
+ // TODO replace with HLS references
CoreClock getClock();
BitVector getRegister(Register r);
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;
private CoreMicroInstructionMemory memory;
private final MicroInstructionMemoryDefinition definition;
+ private final List<Consumer<Object>> memoryBindingListeners;
+
public ModelMicroInstructionMemory(LogicModelModifiable model, MicroInstructionMemoryDefinition definition, String name)
{
super(model, 120, 150, name, "MPM", false);
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();
}
@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<Object> 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<Object> stateChanged)
+ {
+ if (stateID.equals("memory_binding"))
+ memoryBindingListeners.remove(stateChanged);
else
throw new IllegalArgumentException("No high level state with ID " + stateID);
}
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;
private CoreWordAddressableMemory memory;
private MainMemoryDefinition definition;
+ private final List<Consumer<Object>> memoryBindingListeners;
+
public ModelWordAddressableMemory(LogicModelModifiable model, MainMemoryDefinition definition, String name)
{
super(model, 120, 150, name, "RAM", false);
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();
}
@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<Object> 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<Object> stateChanged)
+ {
+ if (stateID.equals("memory_binding"))
+ memoryBindingListeners.remove(stateChanged);
else
throw new IllegalArgumentException("No high level state with ID " + stateID);
}