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.Signal;
import de.neemann.digital.gui.Main;
import java.util.ArrayList;
import java.util.HashMap;
@ -25,7 +24,7 @@ public class ModelAnalyserInfo {
private ArrayList<Signal> inputs;
private ArrayList<Signal> outputs;
private ArrayList<String> 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;
}
}

View File

@ -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<CircuitBuilder> {
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);

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
public void registerNodes(Model model) {
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.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("<?xml version=\"1.0\" encoding=\"utf-8\"?>\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();
}
/**

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.VariableVisitor;
import de.neemann.digital.core.Signal;
import de.neemann.digital.fsm.gui.FSMFrame;
import de.neemann.digital.lang.Lang;
import java.util.*;
@ -47,15 +46,15 @@ public class TransitionTableCreator {
* Creates a new instance
*
* @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.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<>();
}

View File

@ -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,20 +432,11 @@ public class FSMFrame extends JFrame implements ClosingWindowListener.ConfirmSav
toolBar.add(viewHelp);
}
private class StateListener implements GlobalValues.GlobalValueListener {
@Override
public void created(Model model) {
String name = "state";
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();
public void valueCreated(String name, ObservableValue value, Model model) {
if (name.equals(getStateSignalName())) {
value.addObserverToValue(() -> setActiveState(value.getValue()));
setActiveState(value.getValue());
model.addObserver(event -> {
@ -455,29 +446,20 @@ public class FSMFrame extends JFrame implements ClosingWindowListener.ConfirmSav
);
}
}
}
private String getStateSignalName() {
if (filename != null)
return filename.getName();
else
return "state";
}
private void setActiveState(long value) {
if (fsm.setActiveState((int) value))
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
*/

View File

@ -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> 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

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