diff --git a/src/main/java/de/neemann/digital/analyse/ModelAnalyserInfo.java b/src/main/java/de/neemann/digital/analyse/ModelAnalyserInfo.java index f7575a650..6230f7209 100644 --- a/src/main/java/de/neemann/digital/analyse/ModelAnalyserInfo.java +++ b/src/main/java/de/neemann/digital/analyse/ModelAnalyserInfo.java @@ -7,7 +7,6 @@ package de.neemann.digital.analyse; import de.neemann.digital.core.Model; import de.neemann.digital.core.Signal; -import de.neemann.digital.gui.Main; import java.util.ArrayList; import java.util.HashMap; @@ -25,7 +24,7 @@ public class ModelAnalyserInfo { private ArrayList inputs; private ArrayList outputs; private ArrayList pinsWithoutNumber; - private Main.CreatedNotification mainCreatedNotification; + private String stateSignalName; /** * creates a new instance @@ -132,22 +131,6 @@ public class ModelAnalyserInfo { return outputBusMap; } - /** - * Sets the fsm state info; - * - * @param info the info instance - */ - public void setMainCreatedNotification(Main.CreatedNotification info) { - mainCreatedNotification = info; - } - - /** - * @return the fsm state info - */ - public Main.CreatedNotification getMainCreatedNotification() { - return mainCreatedNotification; - } - /** * Gets the init value of a sequential state machine * @@ -171,4 +154,19 @@ public class ModelAnalyserInfo { initValueMap.put(name, value); } + /** + * @return the state variable name, maybe null + */ + public String getStateSignalName() { + return stateSignalName; + } + + /** + * Sets the state variable name + * + * @param stateSignalName the state variable name + */ + public void setStateSignalName(String stateSignalName) { + this.stateSignalName = stateSignalName; + } } diff --git a/src/main/java/de/neemann/digital/builder/circuit/CircuitBuilder.java b/src/main/java/de/neemann/digital/builder/circuit/CircuitBuilder.java index b4c2a852f..9a2b87805 100644 --- a/src/main/java/de/neemann/digital/builder/circuit/CircuitBuilder.java +++ b/src/main/java/de/neemann/digital/builder/circuit/CircuitBuilder.java @@ -6,9 +6,6 @@ package de.neemann.digital.builder.circuit; import de.neemann.digital.analyse.DetermineJKStateMachine; -import de.neemann.digital.core.memory.DataField; -import de.neemann.digital.core.memory.LookUpTable; -import de.neemann.digital.fsm.FSMStateInfo; import de.neemann.digital.analyse.ModelAnalyserInfo; import de.neemann.digital.analyse.expression.*; import de.neemann.digital.analyse.expression.Not; @@ -24,6 +21,8 @@ import de.neemann.digital.core.io.Const; import de.neemann.digital.core.io.In; import de.neemann.digital.core.io.Out; import de.neemann.digital.core.io.Probe; +import de.neemann.digital.core.memory.DataField; +import de.neemann.digital.core.memory.LookUpTable; import de.neemann.digital.core.wiring.Clock; import de.neemann.digital.core.wiring.Splitter; import de.neemann.digital.draw.elements.Circuit; @@ -32,7 +31,6 @@ import de.neemann.digital.draw.elements.VisualElement; import de.neemann.digital.draw.elements.Wire; import de.neemann.digital.draw.graphics.Vector; import de.neemann.digital.draw.shapes.ShapeFactory; -import de.neemann.digital.gui.Main; import de.neemann.digital.lang.Lang; import java.util.*; @@ -445,11 +443,9 @@ public class CircuitBuilder implements BuilderInterface { outSplitterY = addNetConnections(circuit, maxWidth + SIZE * 17, outSplitterY); if (mai != null) { - Main.CreatedNotification mon = mai.getMainCreatedNotification(); - if (mon instanceof FSMStateInfo) { - FSMStateInfo fsmInfo = (FSMStateInfo) mon; - outSplitterY = createStateVar(maxWidth + SIZE * 15, outSplitterY, circuit, fsmInfo.getSignalName()); - } + final String stateVariableName = mai.getStateSignalName(); + if (stateVariableName != null) + outSplitterY = createStateVar(maxWidth + SIZE * 15, outSplitterY, circuit, stateVariableName); } circuit.setModified(false); diff --git a/src/main/java/de/neemann/digital/core/GlobalValues.java b/src/main/java/de/neemann/digital/core/GlobalValues.java new file mode 100644 index 000000000..5e498a210 --- /dev/null +++ b/src/main/java/de/neemann/digital/core/GlobalValues.java @@ -0,0 +1,74 @@ +package de.neemann.digital.core; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; + +/** + * Instance used by the model to share signal values. + * Used by the FSM dialog to indicate the current state. + * Up to now only probe values are shared. + */ +public final class GlobalValues { + private static final Logger LOGGER = LoggerFactory.getLogger(GlobalValues.class); + private static GlobalValues ourInstance = new GlobalValues(); + + /** + * @return returns the global instance + */ + public static GlobalValues getInstance() { + return ourInstance; + } + + private ArrayList listeners = new ArrayList<>(); + + private GlobalValues() { + } + + /** + * Registers a value + * + * @param name the name of the value + * @param value the value itself + * @param model the model the value belongs to + */ + public void register(String name, ObservableValue value, Model model) { + for (GlobalValueListener l : listeners) + l.valueCreated(name, value, model); + } + + /** + * Adds a listener for global values + * + * @param listener the listener to add + */ + public void addListener(GlobalValueListener listener) { + listeners.add(listener); + LOGGER.debug("global value listener added " + listeners.size()); + } + + /** + * Removes a listener for global values + * + * @param listener the listener to remove + */ + public void removeListener(GlobalValueListener listener) { + listeners.remove(listener); + LOGGER.debug("global value listener removed " + listeners.size()); + } + + /** + * A listener for global values + */ + public interface GlobalValueListener { + /** + * Called if a value is created + * + * @param name the name of the value + * @param value the value itself + * @param model the model the value belongs to + */ + void valueCreated(String name, ObservableValue value, Model model); + } +} diff --git a/src/main/java/de/neemann/digital/core/io/Probe.java b/src/main/java/de/neemann/digital/core/io/Probe.java index 67ad09971..e83b8c9e0 100644 --- a/src/main/java/de/neemann/digital/core/io/Probe.java +++ b/src/main/java/de/neemann/digital/core/io/Probe.java @@ -54,6 +54,7 @@ public class Probe implements Element { @Override public void registerNodes(Model model) { model.addSignal(new Signal(label, value).setFormat(format)); + GlobalValues.getInstance().register(label, value, model); } } diff --git a/src/main/java/de/neemann/digital/fsm/FSM.java b/src/main/java/de/neemann/digital/fsm/FSM.java index 329a45135..963578ecf 100644 --- a/src/main/java/de/neemann/digital/fsm/FSM.java +++ b/src/main/java/de/neemann/digital/fsm/FSM.java @@ -13,10 +13,10 @@ import de.neemann.digital.analyse.expression.ExpressionException; import de.neemann.digital.draw.graphics.Graphic; import de.neemann.digital.draw.graphics.Vector; import de.neemann.digital.draw.graphics.VectorFloat; -import de.neemann.digital.fsm.gui.FSMFrame; import de.neemann.digital.lang.Lang; import java.io.*; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -126,7 +126,7 @@ public class FSM { * @throws IOException IOException */ public void save(OutputStream out) throws IOException { - try (Writer w = new OutputStreamWriter(out, "utf-8")) { + try (Writer w = new OutputStreamWriter(out, StandardCharsets.UTF_8)) { XStream xStream = getxStream(); w.write("\n"); xStream.marshal(this, new PrettyPrintWriter(w)); @@ -341,13 +341,13 @@ public class FSM { /** * Creates the truth table which is defined by this finite state machine * - * @param creator the creator of the truth table + * @param stateSignalName the name of the signal used to represent the state * @return the truth table * @throws ExpressionException ExpressionException * @throws FiniteStateMachineException FiniteStateMachineException */ - public TruthTable createTruthTable(FSMFrame creator) throws ExpressionException, FiniteStateMachineException { - return new TransitionTableCreator(this, creator).create(); + public TruthTable createTruthTable(String stateSignalName) throws ExpressionException, FiniteStateMachineException { + return new TransitionTableCreator(this, stateSignalName).create(); } /** diff --git a/src/main/java/de/neemann/digital/fsm/FSMStateInfo.java b/src/main/java/de/neemann/digital/fsm/FSMStateInfo.java deleted file mode 100644 index d185f634e..000000000 --- a/src/main/java/de/neemann/digital/fsm/FSMStateInfo.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2018 Helmut Neemann. - * Use of this source code is governed by the GPL v3 license - * that can be found in the LICENSE file. - */ -package de.neemann.digital.fsm; - -import de.neemann.digital.fsm.gui.FSMFrame; -import de.neemann.digital.gui.Main; - -import java.io.File; - -/** - * The model state info - */ -public class FSMStateInfo implements Main.CreatedNotification { - private final String signalName; - private final FSMFrame fsmFrame; - - /** - * Creates a new instance - * - * @param file the file of the fsm, maybe null - * @param fsmFrame the creator, maybe null - */ - public FSMStateInfo(File file, FSMFrame fsmFrame) { - this.fsmFrame = fsmFrame; - if (file != null) - this.signalName = file.getName(); - else - this.signalName = "state"; - } - - /** - * @return the signal name used for state transfer - */ - public String getSignalName() { - return signalName; - } - - @Override - public void isCreated(Main main) { - if (fsmFrame != null) - fsmFrame.registerTo(main); - } -} diff --git a/src/main/java/de/neemann/digital/fsm/TransitionTableCreator.java b/src/main/java/de/neemann/digital/fsm/TransitionTableCreator.java index 9bea3cfa8..48a96ff69 100644 --- a/src/main/java/de/neemann/digital/fsm/TransitionTableCreator.java +++ b/src/main/java/de/neemann/digital/fsm/TransitionTableCreator.java @@ -12,7 +12,6 @@ import de.neemann.digital.analyse.expression.ExpressionException; import de.neemann.digital.analyse.expression.Variable; import de.neemann.digital.analyse.expression.VariableVisitor; import de.neemann.digital.core.Signal; -import de.neemann.digital.fsm.gui.FSMFrame; import de.neemann.digital.lang.Lang; import java.util.*; @@ -46,16 +45,16 @@ public class TransitionTableCreator { /** * Creates a new instance * - * @param fsm the fsm - * @param creator the creating frame + * @param fsm the fsm + * @param stateSignalName the name if the signal indicating the actual state */ - TransitionTableCreator(FSM fsm, FSMFrame creator) throws FiniteStateMachineException { + TransitionTableCreator(FSM fsm, String stateSignalName) throws FiniteStateMachineException { this.states = fsm.getStates(); this.transitions = fsm.getTransitions(); this.initState = fsm.getInitState(); outputValues = new HashMap<>(); modelAnalyserInfo = new ModelAnalyserInfo(null); - modelAnalyserInfo.setMainCreatedNotification(new FSMStateInfo(fsm.getFile(), creator)); + modelAnalyserInfo.setStateSignalName(stateSignalName); inputSignals = new ArrayList<>(); outputSignals = new ArrayList<>(); } diff --git a/src/main/java/de/neemann/digital/fsm/gui/FSMFrame.java b/src/main/java/de/neemann/digital/fsm/gui/FSMFrame.java index e14a89ef7..8b8c6c861 100644 --- a/src/main/java/de/neemann/digital/fsm/gui/FSMFrame.java +++ b/src/main/java/de/neemann/digital/fsm/gui/FSMFrame.java @@ -5,10 +5,7 @@ */ package de.neemann.digital.fsm.gui; -import de.neemann.digital.core.Model; -import de.neemann.digital.core.ModelEvent; -import de.neemann.digital.core.ObservableValue; -import de.neemann.digital.core.Signal; +import de.neemann.digital.core.*; import de.neemann.digital.core.element.ElementAttributes; import de.neemann.digital.draw.graphics.*; import de.neemann.digital.draw.library.ElementLibrary; @@ -37,7 +34,7 @@ import java.util.prefs.Preferences; /** * The dialog to show the FSM */ -public class FSMFrame extends JFrame implements ClosingWindowListener.ConfirmSave, FSM.ModifiedListener, ModelCreationListener, FileHistory.OpenInterface { +public class FSMFrame extends JFrame implements ClosingWindowListener.ConfirmSave, FSM.ModifiedListener, FileHistory.OpenInterface { private static final Preferences PREFS = Preferences.userRoot().node("dig").node("fsm"); private static final String PREF_FOLDER = "folder"; private static final Icon ICON_NEW = IconCreator.create("document-new.png"); @@ -59,6 +56,7 @@ public class FSMFrame extends JFrame implements ClosingWindowListener.ConfirmSav private File baseFilename; private boolean lastModified; private String probeLabelName; + private GlobalValues.GlobalValueListener stateListener = new StateListener(); /** * Creates a new instance @@ -89,11 +87,13 @@ public class FSMFrame extends JFrame implements ClosingWindowListener.ConfirmSav } }); + GlobalValues.getInstance().addListener(stateListener); addWindowListener(new ClosingWindowListener(this, this)); addWindowListener(new WindowAdapter() { @Override public void windowClosed(WindowEvent windowEvent) { timer.stop(); + GlobalValues.getInstance().removeListener(stateListener); } }); @@ -355,7 +355,7 @@ public class FSMFrame extends JFrame implements ClosingWindowListener.ConfirmSav @Override public void actionPerformed(ActionEvent actionEvent) { try { - new TableDialog(FSMFrame.this, fsm.createTruthTable(FSMFrame.this), library, filename).setVisible(true); + new TableDialog(FSMFrame.this, fsm.createTruthTable(getStateSignalName()), library, filename).setVisible(true); } catch (Exception e) { new ErrorMessage(Lang.get("msg_fsmCantCreateTable")).addCause(e).show(FSMFrame.this); } @@ -432,28 +432,27 @@ public class FSMFrame extends JFrame implements ClosingWindowListener.ConfirmSav toolBar.add(viewHelp); } - @Override - public void created(Model model) { - String name = "state"; + + private class StateListener implements GlobalValues.GlobalValueListener { + @Override + public void valueCreated(String name, ObservableValue value, Model model) { + if (name.equals(getStateSignalName())) { + value.addObserverToValue(() -> setActiveState(value.getValue())); + setActiveState(value.getValue()); + model.addObserver(event -> { + if (event == ModelEvent.STOPPED) + setActiveState(-1); + }, ModelEvent.STOPPED + ); + } + } + } + + private String getStateSignalName() { if (filename != null) - name = filename.getName(); - - Signal found = null; - for (Signal s : model.getSignals()) { - if (s.getName().equals(name)) - found = s; - } - - if (found != null) { - ObservableValue value = found.getValue(); - value.addObserverToValue(() -> setActiveState(value.getValue())); - setActiveState(value.getValue()); - model.addObserver(event -> { - if (event == ModelEvent.STOPPED) - setActiveState(-1); - }, ModelEvent.STOPPED - ); - } + return filename.getName(); + else + return "state"; } private void setActiveState(long value) { @@ -461,23 +460,6 @@ public class FSMFrame extends JFrame implements ClosingWindowListener.ConfirmSav SwingUtilities.invokeLater(fsmComponent::repaint); } - /** - * Registers a circuit window to this fsm - * - * @param main the main window - * @return this for chained calls - */ - public FSMFrame registerTo(Main main) { - main.addModelCreationListener(this); - addWindowListener(new WindowAdapter() { - @Override - public void windowClosed(WindowEvent windowEvent) { - main.removeModelCreationListener(FSMFrame.this); - } - }); - return this; - } - /** * @return the current fsm */ diff --git a/src/main/java/de/neemann/digital/gui/Main.java b/src/main/java/de/neemann/digital/gui/Main.java index 26fefaf34..0df32975b 100644 --- a/src/main/java/de/neemann/digital/gui/Main.java +++ b/src/main/java/de/neemann/digital/gui/Main.java @@ -145,7 +145,6 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS private State runModelMicroState; private JComponent componentOnPane; private LibraryTreeModel treeModel; - private ArrayList modelCreationListener = new ArrayList<>(); /** * Creates a new instance @@ -288,8 +287,6 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS } else setLocationRelativeTo(null); - if (builder.createdNotification != null) - builder.createdNotification.isCreated(this); } private void enableClockShortcut() { @@ -1150,7 +1147,6 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS new FSMFrame(Main.this, library) .setBaseFileName(filename) .setProbeLabelName(foundName) - .registerTo(Main.this) .setVisible(true); } } @@ -1158,24 +1154,6 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS .createJMenuItem()); } - /** - * Adds a model creation listener - * - * @param listener the listener to add - */ - public void addModelCreationListener(ModelCreationListener listener) { - modelCreationListener.add(listener); - } - - /** - * Removes a model creation listener - * - * @param listener the listener to remove - */ - public void removeModelCreationListener(ModelCreationListener listener) { - modelCreationListener.remove(listener); - } - private void orderMeasurements() { try { Model m = new ModelCreator(circuitComponent.getCircuit(), library).createModel(false); @@ -1325,9 +1303,6 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS model.init(); - for (ModelCreationListener mcl : modelCreationListener) - mcl.created(model); - if (updateEvent == ModelEvent.MICROSTEP) doStep.setEnabled(model.needsUpdate()); @@ -1824,7 +1799,6 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS private boolean allowAllFileActions = true; private File baseFileName; private boolean keepPrefMainFile; - private CreatedNotification createdNotification; /** * @param fileToOpen the file to open @@ -1892,17 +1866,6 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS return this; } - /** - * Sets a open notification - * - * @param createdNotification createdNotification - * @return this for chained calls - */ - public MainBuilder setCreatedNotification(CreatedNotification createdNotification) { - this.createdNotification = createdNotification; - return this; - } - /** * Creates a new Main instance * @@ -1921,18 +1884,6 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS } - /** - * Notification if a main frame is created - */ - public interface CreatedNotification { - /** - * Called if main frae is created - * - * @param main main - */ - void isCreated(Main main); - } - private class ModelKeyListener extends KeyAdapter { @Override diff --git a/src/main/java/de/neemann/digital/gui/ModelCreationListener.java b/src/main/java/de/neemann/digital/gui/ModelCreationListener.java deleted file mode 100644 index f974f174b..000000000 --- a/src/main/java/de/neemann/digital/gui/ModelCreationListener.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2018 Helmut Neemann. - * Use of this source code is governed by the GPL v3 license - * that can be found in the LICENSE file. - */ -package de.neemann.digital.gui; - -import de.neemann.digital.core.Model; - -/** - * Listener notified every time a model is created - */ -public interface ModelCreationListener { - /** - * Called if a model is created - * - * @param model the model created - */ - void created(Model model); -} diff --git a/src/main/java/de/neemann/digital/gui/components/table/TableDialog.java b/src/main/java/de/neemann/digital/gui/components/table/TableDialog.java index be8b91e7c..bf84a1c3e 100644 --- a/src/main/java/de/neemann/digital/gui/components/table/TableDialog.java +++ b/src/main/java/de/neemann/digital/gui/components/table/TableDialog.java @@ -518,15 +518,11 @@ public class TableDialog extends JDialog { .create(lastGeneratedExpressions); Circuit circuit = circuitBuilder.createCircuit(); - Main.CreatedNotification mon = null; - if (modelAnalyzerInfo != null) - mon = modelAnalyzerInfo.getMainCreatedNotification(); new Main.MainBuilder() .setParent(TableDialog.this) .setLibrary(library) .setCircuit(circuit) .setBaseFileName(filename) - .setCreatedNotification(mon) .openLater(); } catch (ExpressionException | FormatterException | RuntimeException e) { new ErrorMessage(Lang.get("msg_errorDuringCalculation")).addCause(e).show(this);