Refactoring of signal state sharing used by the FSM frame.

This commit is contained in:
hneemann 2019-04-05 11:16:26 +02:00
parent 8d1b34cc5b
commit fc41cae32a
11 changed files with 131 additions and 200 deletions

View File

@ -7,7 +7,6 @@ package de.neemann.digital.analyse;
import de.neemann.digital.core.Model; import de.neemann.digital.core.Model;
import de.neemann.digital.core.Signal; import de.neemann.digital.core.Signal;
import de.neemann.digital.gui.Main;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -25,7 +24,7 @@ public class ModelAnalyserInfo {
private ArrayList<Signal> inputs; private ArrayList<Signal> inputs;
private ArrayList<Signal> outputs; private ArrayList<Signal> outputs;
private ArrayList<String> pinsWithoutNumber; private ArrayList<String> pinsWithoutNumber;
private Main.CreatedNotification mainCreatedNotification; private String stateSignalName;
/** /**
* creates a new instance * creates a new instance
@ -132,22 +131,6 @@ public class ModelAnalyserInfo {
return outputBusMap; 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 * Gets the init value of a sequential state machine
* *
@ -171,4 +154,19 @@ public class ModelAnalyserInfo {
initValueMap.put(name, value); 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;
}
} }

View File

@ -6,9 +6,6 @@
package de.neemann.digital.builder.circuit; package de.neemann.digital.builder.circuit;
import de.neemann.digital.analyse.DetermineJKStateMachine; 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.ModelAnalyserInfo;
import de.neemann.digital.analyse.expression.*; import de.neemann.digital.analyse.expression.*;
import de.neemann.digital.analyse.expression.Not; 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.In;
import de.neemann.digital.core.io.Out; import de.neemann.digital.core.io.Out;
import de.neemann.digital.core.io.Probe; 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.Clock;
import de.neemann.digital.core.wiring.Splitter; import de.neemann.digital.core.wiring.Splitter;
import de.neemann.digital.draw.elements.Circuit; 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.elements.Wire;
import de.neemann.digital.draw.graphics.Vector; import de.neemann.digital.draw.graphics.Vector;
import de.neemann.digital.draw.shapes.ShapeFactory; import de.neemann.digital.draw.shapes.ShapeFactory;
import de.neemann.digital.gui.Main;
import de.neemann.digital.lang.Lang; import de.neemann.digital.lang.Lang;
import java.util.*; import java.util.*;
@ -445,11 +443,9 @@ public class CircuitBuilder implements BuilderInterface<CircuitBuilder> {
outSplitterY = addNetConnections(circuit, maxWidth + SIZE * 17, outSplitterY); outSplitterY = addNetConnections(circuit, maxWidth + SIZE * 17, outSplitterY);
if (mai != null) { if (mai != null) {
Main.CreatedNotification mon = mai.getMainCreatedNotification(); final String stateVariableName = mai.getStateSignalName();
if (mon instanceof FSMStateInfo) { if (stateVariableName != null)
FSMStateInfo fsmInfo = (FSMStateInfo) mon; outSplitterY = createStateVar(maxWidth + SIZE * 15, outSplitterY, circuit, stateVariableName);
outSplitterY = createStateVar(maxWidth + SIZE * 15, outSplitterY, circuit, fsmInfo.getSignalName());
}
} }
circuit.setModified(false); circuit.setModified(false);

View File

@ -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<GlobalValueListener> 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);
}
}

View File

@ -54,6 +54,7 @@ public class Probe implements Element {
@Override @Override
public void registerNodes(Model model) { public void registerNodes(Model model) {
model.addSignal(new Signal(label, value).setFormat(format)); model.addSignal(new Signal(label, value).setFormat(format));
GlobalValues.getInstance().register(label, value, model);
} }
} }

View File

@ -13,10 +13,10 @@ import de.neemann.digital.analyse.expression.ExpressionException;
import de.neemann.digital.draw.graphics.Graphic; import de.neemann.digital.draw.graphics.Graphic;
import de.neemann.digital.draw.graphics.Vector; import de.neemann.digital.draw.graphics.Vector;
import de.neemann.digital.draw.graphics.VectorFloat; import de.neemann.digital.draw.graphics.VectorFloat;
import de.neemann.digital.fsm.gui.FSMFrame;
import de.neemann.digital.lang.Lang; import de.neemann.digital.lang.Lang;
import java.io.*; import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -126,7 +126,7 @@ public class FSM {
* @throws IOException IOException * @throws IOException IOException
*/ */
public void save(OutputStream out) throws 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(); XStream xStream = getxStream();
w.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); w.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
xStream.marshal(this, new PrettyPrintWriter(w)); 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 * 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 * @return the truth table
* @throws ExpressionException ExpressionException * @throws ExpressionException ExpressionException
* @throws FiniteStateMachineException FiniteStateMachineException * @throws FiniteStateMachineException FiniteStateMachineException
*/ */
public TruthTable createTruthTable(FSMFrame creator) throws ExpressionException, FiniteStateMachineException { public TruthTable createTruthTable(String stateSignalName) throws ExpressionException, FiniteStateMachineException {
return new TransitionTableCreator(this, creator).create(); return new TransitionTableCreator(this, stateSignalName).create();
} }
/** /**

View File

@ -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);
}
}

View File

@ -12,7 +12,6 @@ import de.neemann.digital.analyse.expression.ExpressionException;
import de.neemann.digital.analyse.expression.Variable; import de.neemann.digital.analyse.expression.Variable;
import de.neemann.digital.analyse.expression.VariableVisitor; import de.neemann.digital.analyse.expression.VariableVisitor;
import de.neemann.digital.core.Signal; import de.neemann.digital.core.Signal;
import de.neemann.digital.fsm.gui.FSMFrame;
import de.neemann.digital.lang.Lang; import de.neemann.digital.lang.Lang;
import java.util.*; import java.util.*;
@ -46,16 +45,16 @@ public class TransitionTableCreator {
/** /**
* Creates a new instance * Creates a new instance
* *
* @param fsm the fsm * @param fsm the fsm
* @param creator the creating frame * @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.states = fsm.getStates();
this.transitions = fsm.getTransitions(); this.transitions = fsm.getTransitions();
this.initState = fsm.getInitState(); this.initState = fsm.getInitState();
outputValues = new HashMap<>(); outputValues = new HashMap<>();
modelAnalyserInfo = new ModelAnalyserInfo(null); modelAnalyserInfo = new ModelAnalyserInfo(null);
modelAnalyserInfo.setMainCreatedNotification(new FSMStateInfo(fsm.getFile(), creator)); modelAnalyserInfo.setStateSignalName(stateSignalName);
inputSignals = new ArrayList<>(); inputSignals = new ArrayList<>();
outputSignals = new ArrayList<>(); outputSignals = new ArrayList<>();
} }

View File

@ -5,10 +5,7 @@
*/ */
package de.neemann.digital.fsm.gui; package de.neemann.digital.fsm.gui;
import de.neemann.digital.core.Model; import de.neemann.digital.core.*;
import de.neemann.digital.core.ModelEvent;
import de.neemann.digital.core.ObservableValue;
import de.neemann.digital.core.Signal;
import de.neemann.digital.core.element.ElementAttributes; import de.neemann.digital.core.element.ElementAttributes;
import de.neemann.digital.draw.graphics.*; import de.neemann.digital.draw.graphics.*;
import de.neemann.digital.draw.library.ElementLibrary; import de.neemann.digital.draw.library.ElementLibrary;
@ -37,7 +34,7 @@ import java.util.prefs.Preferences;
/** /**
* The dialog to show the FSM * 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 Preferences PREFS = Preferences.userRoot().node("dig").node("fsm");
private static final String PREF_FOLDER = "folder"; private static final String PREF_FOLDER = "folder";
private static final Icon ICON_NEW = IconCreator.create("document-new.png"); 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 File baseFilename;
private boolean lastModified; private boolean lastModified;
private String probeLabelName; private String probeLabelName;
private GlobalValues.GlobalValueListener stateListener = new StateListener();
/** /**
* Creates a new instance * 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 ClosingWindowListener(this, this));
addWindowListener(new WindowAdapter() { addWindowListener(new WindowAdapter() {
@Override @Override
public void windowClosed(WindowEvent windowEvent) { public void windowClosed(WindowEvent windowEvent) {
timer.stop(); timer.stop();
GlobalValues.getInstance().removeListener(stateListener);
} }
}); });
@ -355,7 +355,7 @@ public class FSMFrame extends JFrame implements ClosingWindowListener.ConfirmSav
@Override @Override
public void actionPerformed(ActionEvent actionEvent) { public void actionPerformed(ActionEvent actionEvent) {
try { 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) { } catch (Exception e) {
new ErrorMessage(Lang.get("msg_fsmCantCreateTable")).addCause(e).show(FSMFrame.this); 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); toolBar.add(viewHelp);
} }
@Override
public void created(Model model) { private class StateListener implements GlobalValues.GlobalValueListener {
String name = "state"; @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) if (filename != null)
name = filename.getName(); return filename.getName();
else
Signal found = null; return "state";
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
);
}
} }
private void setActiveState(long value) { private void setActiveState(long value) {
@ -461,23 +460,6 @@ public class FSMFrame extends JFrame implements ClosingWindowListener.ConfirmSav
SwingUtilities.invokeLater(fsmComponent::repaint); 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 * @return the current fsm
*/ */

View File

@ -145,7 +145,6 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
private State runModelMicroState; private State runModelMicroState;
private JComponent componentOnPane; private JComponent componentOnPane;
private LibraryTreeModel treeModel; private LibraryTreeModel treeModel;
private ArrayList<ModelCreationListener> modelCreationListener = new ArrayList<>();
/** /**
* Creates a new instance * Creates a new instance
@ -288,8 +287,6 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
} else } else
setLocationRelativeTo(null); setLocationRelativeTo(null);
if (builder.createdNotification != null)
builder.createdNotification.isCreated(this);
} }
private void enableClockShortcut() { private void enableClockShortcut() {
@ -1150,7 +1147,6 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
new FSMFrame(Main.this, library) new FSMFrame(Main.this, library)
.setBaseFileName(filename) .setBaseFileName(filename)
.setProbeLabelName(foundName) .setProbeLabelName(foundName)
.registerTo(Main.this)
.setVisible(true); .setVisible(true);
} }
} }
@ -1158,24 +1154,6 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
.createJMenuItem()); .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() { private void orderMeasurements() {
try { try {
Model m = new ModelCreator(circuitComponent.getCircuit(), library).createModel(false); Model m = new ModelCreator(circuitComponent.getCircuit(), library).createModel(false);
@ -1325,9 +1303,6 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
model.init(); model.init();
for (ModelCreationListener mcl : modelCreationListener)
mcl.created(model);
if (updateEvent == ModelEvent.MICROSTEP) if (updateEvent == ModelEvent.MICROSTEP)
doStep.setEnabled(model.needsUpdate()); doStep.setEnabled(model.needsUpdate());
@ -1824,7 +1799,6 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
private boolean allowAllFileActions = true; private boolean allowAllFileActions = true;
private File baseFileName; private File baseFileName;
private boolean keepPrefMainFile; private boolean keepPrefMainFile;
private CreatedNotification createdNotification;
/** /**
* @param fileToOpen the file to open * @param fileToOpen the file to open
@ -1892,17 +1866,6 @@ public final class Main extends JFrame implements ClosingWindowListener.ConfirmS
return this; 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 * 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 { private class ModelKeyListener extends KeyAdapter {
@Override @Override

View File

@ -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);
}

View File

@ -518,15 +518,11 @@ public class TableDialog extends JDialog {
.create(lastGeneratedExpressions); .create(lastGeneratedExpressions);
Circuit circuit = circuitBuilder.createCircuit(); Circuit circuit = circuitBuilder.createCircuit();
Main.CreatedNotification mon = null;
if (modelAnalyzerInfo != null)
mon = modelAnalyzerInfo.getMainCreatedNotification();
new Main.MainBuilder() new Main.MainBuilder()
.setParent(TableDialog.this) .setParent(TableDialog.this)
.setLibrary(library) .setLibrary(library)
.setCircuit(circuit) .setCircuit(circuit)
.setBaseFileName(filename) .setBaseFileName(filename)
.setCreatedNotification(mon)
.openLater(); .openLater();
} catch (ExpressionException | FormatterException | RuntimeException e) { } catch (ExpressionException | FormatterException | RuntimeException e) {
new ErrorMessage(Lang.get("msg_errorDuringCalculation")).addCause(e).show(this); new ErrorMessage(Lang.get("msg_errorDuringCalculation")).addCause(e).show(this);