1121 lines
44 KiB
Java

package de.neemann.digital.gui;
import de.neemann.digital.analyse.AnalyseException;
import de.neemann.digital.analyse.ModelAnalyser;
import de.neemann.digital.analyse.TruthTable;
import de.neemann.digital.analyse.expression.format.FormatToExpression;
import de.neemann.digital.core.*;
import de.neemann.digital.core.element.ElementAttributes;
import de.neemann.digital.core.element.Key;
import de.neemann.digital.core.element.Keys;
import de.neemann.digital.core.io.In;
import de.neemann.digital.core.io.Out;
import de.neemann.digital.core.memory.ROM;
import de.neemann.digital.core.wiring.Clock;
import de.neemann.digital.draw.elements.Circuit;
import de.neemann.digital.draw.elements.ElementOrder;
import de.neemann.digital.draw.elements.PinException;
import de.neemann.digital.draw.elements.VisualElement;
import de.neemann.digital.draw.graphics.*;
import de.neemann.digital.draw.library.CustomElement;
import de.neemann.digital.draw.library.ElementLibrary;
import de.neemann.digital.draw.library.ElementNotFoundException;
import de.neemann.digital.draw.model.ModelCreator;
import de.neemann.digital.draw.model.RealTimeClock;
import de.neemann.digital.draw.shapes.ShapeFactory;
import de.neemann.digital.gui.components.*;
import de.neemann.digital.gui.components.data.DataSetDialog;
import de.neemann.digital.gui.components.expression.ExpressionDialog;
import de.neemann.digital.gui.components.table.TableDialog;
import de.neemann.digital.gui.components.testing.TestResultDialog;
import de.neemann.digital.gui.remote.DigitalHandler;
import de.neemann.digital.gui.remote.RemoteException;
import de.neemann.digital.gui.remote.RemoteSever;
import de.neemann.digital.gui.state.State;
import de.neemann.digital.gui.state.StateManager;
import de.neemann.digital.gui.sync.LockSync;
import de.neemann.digital.gui.sync.NoSync;
import de.neemann.digital.gui.sync.Sync;
import de.neemann.digital.lang.Lang;
import de.neemann.digital.testing.TestCaseElement;
import de.neemann.digital.testing.TestingDataException;
import de.neemann.gui.*;
import de.neemann.gui.language.Language;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ScheduledThreadPoolExecutor;
/**
* The main frame of the Digital Simulator
*
* @author hneemann
*/
public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, ErrorStopper, FileHistory.OpenInterface, DigitalRemoteInterface, StatusInterface {
private static final ArrayList<Key> ATTR_LIST = new ArrayList<>();
private static boolean experimental;
private static File lastExportDirectory;
/**
* @return true if experimental features are enabled
*/
public static boolean enableExperimental() {
return experimental;
}
static {
ATTR_LIST.add(Keys.SHOW_DATA_TABLE);
ATTR_LIST.add(Keys.SHOW_DATA_GRAPH);
ATTR_LIST.add(Keys.SHOW_DATA_GRAPH_MICRO);
}
private static final String MESSAGE = Lang.get("message");
private static final Icon ICON_RUN = IconCreator.create("media-playback-start.png");
private static final Icon ICON_MICRO = IconCreator.create("media-playback-start-2.png");
private static final Icon ICON_TEST = IconCreator.create("media-playback-start-T.png");
private static final Icon ICON_STEP = IconCreator.create("media-seek-forward.png");
private static final Icon ICON_STOP = IconCreator.create("media-playback-stop.png");
private static final Icon ICON_NEW = IconCreator.create("document-new.png");
private static final Icon ICON_OPEN = IconCreator.create("document-open.png");
private static final Icon ICON_OPEN_WIN = IconCreator.create("document-open-new.png");
private static final Icon ICON_SAVE = IconCreator.create("document-save.png");
private static final Icon ICON_SAVE_AS = IconCreator.create("document-save-as.png");
private static final Icon ICON_FAST = IconCreator.create("media-skip-forward.png");
private static final Icon ICON_EXPAND = IconCreator.create("View-zoom-fit.png");
private static final Icon ICON_ZOOMIN = IconCreator.create("View-zoom-in.png");
private static final Icon ICON_ZOOMOUT = IconCreator.create("View-zoom-out.png");
private static final Icon ICON_HELP = IconCreator.create("help.png");
private final CircuitComponent circuitComponent;
private final ToolTipAction save;
private ToolTipAction doStep;
private ToolTipAction runToBreakAction;
private final ElementLibrary library;
private final LibrarySelector librarySelector;
private final ShapeFactory shapeFactory;
private final SavedListener savedListener;
private final JLabel statusLabel;
private final StateManager stateManager = new StateManager();
private final ElementAttributes settings = new ElementAttributes();
private final ScheduledThreadPoolExecutor timerExecuter = new ScheduledThreadPoolExecutor(1);
private final WindowPosManager windowPosManager = new WindowPosManager();
private File lastFilename;
private File filename;
private FileHistory fileHistory;
private Sync modelSync;
private Model model;
private ModelCreator modelCreator;
private boolean realtimeClockRunning;
private State stoppedState;
private RunModelState runModelState;
private State runModelMicroState;
private Main() {
this(null, null, null, null);
}
/**
* Creates a new instance
*
* @param parent the parent component
* @param fileToOpen a file to open
* @param savedListener a listener which is notified if the file is changed on disk
*/
public Main(Component parent, File fileToOpen, SavedListener savedListener, ElementLibrary library) {
this(parent, fileToOpen, savedListener, library, null);
}
/**
* Creates a new instance
*
* @param parent the parent component
* @param circuit circuit to show
*/
public Main(Component parent, Circuit circuit) {
this(parent, null, null, null, circuit);
}
/**
* Creates a new instance
*
* @param parent the parent component
* @param fileToOpen a file to open
* @param savedListener a listener which is notified if the file is changed on disk
* @param circuit circuit to show
*/
private Main(Component parent, File fileToOpen, SavedListener savedListener, ElementLibrary parentsLibrary, Circuit circuit) {
super(Lang.get("digital"));
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
setIconImages(IconCreator.createImages("icon32.png", "icon64.png", "icon128.png"));
this.savedListener = savedListener;
if (parentsLibrary == null) library = new ElementLibrary();
else this.library = parentsLibrary;
shapeFactory = new ShapeFactory(library, Settings.getInstance().get(Keys.SETTINGS_IEEE_SHAPES));
fileHistory = new FileHistory(this);
final boolean normalMode = savedListener == null;
if (circuit != null) {
circuitComponent = new CircuitComponent(this, library, shapeFactory, savedListener);
SwingUtilities.invokeLater(() -> circuitComponent.setCircuit(circuit));
} else {
circuitComponent = new CircuitComponent(this, library, shapeFactory, savedListener);
if (fileToOpen != null) {
SwingUtilities.invokeLater(() -> loadFile(fileToOpen, false));
} else {
File name = fileHistory.getMostRecent();
if (name != null) {
SwingUtilities.invokeLater(() -> loadFile(name, false));
}
}
}
getContentPane().add(circuitComponent);
statusLabel = new JLabel(" ");
getContentPane().add(statusLabel, BorderLayout.SOUTH);
addWindowListener(new ClosingWindowListener(this, this));
addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
clearModelDescription(); // stop model timer if running
timerExecuter.shutdown();
}
});
setupStates();
JMenuBar menuBar = new JMenuBar();
JToolBar toolBar = new JToolBar();
save = createFileMenu(menuBar, toolBar, normalMode);
toolBar.addSeparator();
createViewMenu(menuBar, toolBar);
toolBar.addSeparator();
createEditMenu(menuBar);
toolBar.add(circuitComponent.getDeleteAction().createJButtonNoText());
toolBar.addSeparator();
createStartMenu(menuBar, toolBar);
createAnalyseMenu(menuBar);
toolBar.addSeparator();
librarySelector = new LibrarySelector(library, shapeFactory);
menuBar.add(librarySelector.buildMenu(new InsertHistory(toolBar), circuitComponent));
getContentPane().add(toolBar, BorderLayout.NORTH);
setJMenuBar(menuBar);
JMenu help = InfoDialog.getInstance().addToFrame(this, MESSAGE);
help.add(new ToolTipAction(Lang.get("menu_help_elements")) {
@Override
public void actionPerformed(ActionEvent actionEvent) {
try {
new ElementHelpDialog(Main.this, library, shapeFactory).setVisible(true);
} catch (NodeException | PinException e) {
new ErrorMessage(Lang.get("msg_creatingHelp")).addCause(e).show(Main.this);
}
}
}.setToolTip(Lang.get("menu_help_elements_tt")).createJMenuItem());
setPreferredSize(new Dimension(1024, 768));
pack();
setLocationRelativeTo(parent);
}
private void createViewMenu(JMenuBar menuBar, JToolBar toolBar) {
ToolTipAction maximize = new ToolTipAction(Lang.get("menu_maximize"), ICON_EXPAND) {
@Override
public void actionPerformed(ActionEvent e) {
circuitComponent.fitCircuit();
}
};
ToolTipAction zoomIn = new ToolTipAction(Lang.get("menu_zoomIn"), ICON_ZOOMIN) {
@Override
public void actionPerformed(ActionEvent e) {
circuitComponent.scaleCircuit(1.25);
}
};
ToolTipAction zoomOut = new ToolTipAction(Lang.get("menu_zoomOut"), ICON_ZOOMOUT) {
@Override
public void actionPerformed(ActionEvent e) {
circuitComponent.scaleCircuit(0.8);
}
};
ToolTipAction viewHelp = new ToolTipAction(Lang.get("menu_viewHelp"), ICON_HELP) {
@Override
public void actionPerformed(ActionEvent e) {
final Circuit circuit = circuitComponent.getCircuit();
final String name = Lang.get("msg_actualCircuit");
File file = filename;
if (file == null)
file = new File(name);
try {
ElementLibrary.ElementTypeDescriptionCustom description =
new ElementLibrary.ElementTypeDescriptionCustom(file,
attributes -> new CustomElement(circuit, library, filename),
circuit.getAttributes(), circuit.getInputNames());
description.setShortName(name);
description.setDescription(circuit.getAttributes().get(Keys.DESCRIPTION));
new ElementHelpDialog(Main.this, description, circuit.getAttributes()).setVisible(true);
} catch (PinException | NodeException e1) {
new ErrorMessage(Lang.get("msg_creatingHelp")).addCause(e1).show(Main.this);
}
}
}.setToolTip(Lang.get("menu_viewHelp_tt"));
toolBar.add(viewHelp.createJButtonNoText());
toolBar.add(zoomIn.createJButtonNoText());
toolBar.add(zoomOut.createJButtonNoText());
toolBar.add(maximize.createJButtonNoText());
JMenu view = new JMenu(Lang.get("menu_view"));
menuBar.add(view);
view.add(maximize.createJMenuItem());
view.add(zoomOut.createJMenuItem());
view.add(zoomIn.createJMenuItem());
view.addSeparator();
view.add(viewHelp.createJMenuItem());
}
/**
* Creates the file menu and adds it to menu and toolbar
*
* @param menuBar the menuBar
* @param toolBar the toolBar
* @param normalMode if false, menu is added in nested mode
* @return the save action
*/
private ToolTipAction createFileMenu(JMenuBar menuBar, JToolBar toolBar, boolean normalMode) {
ToolTipAction newFile = new ToolTipAction(Lang.get("menu_new"), ICON_NEW) {
@Override
public void actionPerformed(ActionEvent e) {
if (ClosingWindowListener.checkForSave(Main.this, Main.this)) {
setFilename(null, true);
circuitComponent.setCircuit(new Circuit());
windowPosManager.closeAll();
}
}
}.setActive(normalMode);
ToolTipAction open = new ToolTipAction(Lang.get("menu_open"), ICON_OPEN) {
@Override
public void actionPerformed(ActionEvent e) {
if (ClosingWindowListener.checkForSave(Main.this, Main.this)) {
JFileChooser fc = getJFileChooser(lastFilename);
if (fc.showOpenDialog(Main.this) == JFileChooser.APPROVE_OPTION) {
loadFile(fc.getSelectedFile(), true);
}
}
}
}.setActive(normalMode);
ToolTipAction openWin = new ToolTipAction(Lang.get("menu_openWin"), ICON_OPEN_WIN) {
@Override
public void actionPerformed(ActionEvent e) {
JFileChooser fc = getJFileChooser(lastFilename);
if (fc.showOpenDialog(Main.this) == JFileChooser.APPROVE_OPTION) {
Main m = new Main(Main.this, fc.getSelectedFile(), null, null);
m.setLocationRelativeTo(Main.this);
m.setVisible(true);
}
}
}.setToolTip(Lang.get("menu_openWin_tt")).setActive(normalMode);
JMenu openRecent = new JMenu(Lang.get("menu_openRecent"));
fileHistory.setMenu(openRecent);
openRecent.setEnabled(normalMode);
ToolTipAction saveas = new ToolTipAction(Lang.get("menu_saveAs"), ICON_SAVE_AS) {
@Override
public void actionPerformed(ActionEvent e) {
JFileChooser fc = getJFileChooser(lastFilename);
if (fc.showSaveDialog(Main.this) == JFileChooser.APPROVE_OPTION) {
saveFile(fc.getSelectedFile(), normalMode);
}
}
}.setActive(normalMode);
ToolTipAction save = new ToolTipAction(Lang.get("menu_save"), ICON_SAVE) {
@Override
public void actionPerformed(ActionEvent e) {
if (filename == null)
saveas.actionPerformed(e);
else
saveFile(filename, normalMode);
}
};
JMenu export = new JMenu(Lang.get("menu_export"));
export.add(new ExportAction(Lang.get("menu_exportSVG"), "svg", GraphicSVGIndex::new));
export.add(new ExportAction(Lang.get("menu_exportSVGLaTex"), "svg", GraphicSVGLaTeX::new));
export.add(new ExportAction(Lang.get("menu_exportPNGSmall"), "png", (out, min, max) -> GraphicsImage.create(out, min, max, "PNG", 1)));
export.add(new ExportAction(Lang.get("menu_exportPNGLarge"), "png", (out, min, max) -> GraphicsImage.create(out, min, max, "PNG", 2)));
JMenu file = new JMenu(Lang.get("menu_file"));
menuBar.add(file);
file.add(newFile);
file.add(openRecent);
file.add(open);
file.add(openWin);
file.add(save);
file.add(saveas);
file.add(export);
toolBar.add(newFile.createJButtonNoText());
toolBar.add(open.createJButtonNoText());
toolBar.add(save.createJButtonNoText());
return save;
}
/**
* Creates the edit menu
*
* @param menuBar the menu bar
*/
private void createEditMenu(JMenuBar menuBar) {
JMenu edit = new JMenu(Lang.get("menu_edit"));
menuBar.add(edit);
ToolTipAction orderInputs = new ToolTipAction(Lang.get("menu_orderInputs")) {
@Override
public void actionPerformed(ActionEvent e) {
ElementOrder o = new ElementOrder(circuitComponent.getCircuit(),
element -> element.equalsDescription(In.DESCRIPTION)
|| element.equalsDescription(Clock.DESCRIPTION));
new ElementOrderer<>(Main.this, Lang.get("menu_orderInputs"), o).showDialog();
}
}.setToolTip(Lang.get("menu_orderInputs_tt"));
ToolTipAction orderOutputs = new ToolTipAction(Lang.get("menu_orderOutputs")) {
@Override
public void actionPerformed(ActionEvent e) {
ElementOrder o = new ElementOrder(circuitComponent.getCircuit(),
element -> element.equalsDescription(Out.DESCRIPTION)
|| element.equalsDescription(Out.LEDDESCRIPTION));
new ElementOrderer<>(Main.this, Lang.get("menu_orderOutputs"), o).showDialog();
}
}.setToolTip(Lang.get("menu_orderOutputs_tt"));
ToolTipAction orderMeasurements = new ToolTipAction(Lang.get("menu_orderMeasurements")) {
@Override
public void actionPerformed(ActionEvent e) {
orderMeasurements();
}
}.setToolTip(Lang.get("menu_orderMeasurements_tt"));
ToolTipAction editAttributes = new ToolTipAction(Lang.get("menu_editAttributes")) {
@Override
public void actionPerformed(ActionEvent e) {
circuitComponent.getCircuit().editAttributes(Main.this);
}
}.setToolTip(Lang.get("menu_editAttributes_tt"));
ToolTipAction editSettings = new ToolTipAction(Lang.get("menu_editSettings")) {
@Override
public void actionPerformed(ActionEvent e) {
Language oldLang = Settings.getInstance().get(Keys.SETTINGS_LANGUAGE);
if (new AttributeDialog(Main.this, Settings.SETTINGS_KEYS, Settings.getInstance().getAttributes()).showDialog()) {
FormatToExpression.setDefaultFormat(Settings.getInstance().get(Keys.SETTINGS_EXPRESSION_FORMAT));
final Language newLang = Settings.getInstance().getAttributes().get(Keys.SETTINGS_LANGUAGE);
if (!newLang.equals(oldLang)) {
Lang.setLanguage(newLang);
JOptionPane.showMessageDialog(Main.this, Lang.get("msg_restartNeeded"));
}
}
}
}.setToolTip(Lang.get("menu_editSettings_tt"));
ToolTipAction actualToDefault = new ToolTipAction(Lang.get("menu_actualToDefault")) {
@Override
public void actionPerformed(ActionEvent e) {
circuitComponent.getCircuit().actualToDefault();
stoppedState.enter();
}
}.setToolTip(Lang.get("menu_actualToDefault_tt"));
ToolTipAction unprogramAllFuses = new ToolTipAction(Lang.get("menu_unprogramAllFuses")) {
@Override
public void actionPerformed(ActionEvent e) {
circuitComponent.getCircuit().unprogramAllFuses();
stoppedState.enter();
}
}.setToolTip(Lang.get("menu_unprogramAllFuses_tt"));
edit.add(editAttributes.createJMenuItem());
edit.add(actualToDefault.createJMenuItem());
edit.add(unprogramAllFuses.createJMenuItem());
edit.addSeparator();
edit.add(orderInputs.createJMenuItem());
edit.add(orderOutputs.createJMenuItem());
edit.add(orderMeasurements.createJMenuItem());
edit.addSeparator();
JMenuItem copyItem = new JMenuItem(circuitComponent.getCopyAction());
copyItem.setAccelerator(KeyStroke.getKeyStroke('C', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
edit.add(copyItem);
JMenuItem pasteItem = new JMenuItem(circuitComponent.getPasteAction());
pasteItem.setAccelerator(KeyStroke.getKeyStroke('V', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
edit.add(pasteItem);
JMenuItem rotateItem = new JMenuItem(circuitComponent.getRotateAction());
rotateItem.setAccelerator(KeyStroke.getKeyStroke('R'));
edit.add(rotateItem);
edit.addSeparator();
edit.add(editSettings.createJMenuItem());
}
/**
* Creates the start menu
*
* @param menuBar the menu bar
* @param toolBar the tool bar
*/
private void createStartMenu(JMenuBar menuBar, JToolBar toolBar) {
doStep = new ToolTipAction(Lang.get("menu_step"), ICON_STEP) {
@Override
public void actionPerformed(ActionEvent e) {
try {
model.doMicroStep(false);
circuitComponent.removeHighLighted();
modelCreator.addNodeElementsTo(model.nodesToUpdate(), circuitComponent.getHighLighted());
circuitComponent.hasChanged();
doStep.setEnabled(model.needsUpdate());
} catch (Exception e1) {
SwingUtilities.invokeLater(
new ErrorMessage(Lang.get("msg_errorCalculatingStep")).addCause(e1).setComponent(Main.this)
);
}
}
}.setToolTip(Lang.get("menu_step_tt"));
ToolTipAction runModelAction = runModelState.createToolTipAction(Lang.get("menu_run"), ICON_RUN)
.setToolTip(Lang.get("menu_run_tt"));
ToolTipAction runModelMicroAction = runModelMicroState.createToolTipAction(Lang.get("menu_micro"), ICON_MICRO)
.setToolTip(Lang.get("menu_micro_tt"));
runToBreakAction = new ToolTipAction(Lang.get("menu_fast"), ICON_FAST) {
@Override
public void actionPerformed(ActionEvent e) {
try {
int i = model.runToBreak();
circuitComponent.hasChanged();
statusLabel.setText(Lang.get("stat_clocks", i));
} catch (NodeException e1) {
stoppedState.enter();
new ErrorMessage(Lang.get("msg_fastRunError")).addCause(e1).show(Main.this);
}
}
}.setToolTip(Lang.get("menu_fast_tt")).setActive(false);
ToolTipAction stoppedStateAction = stoppedState.createToolTipAction(Lang.get("menu_element"), ICON_STOP).setToolTip(Lang.get("menu_element_tt"));
ToolTipAction runTests = new ToolTipAction(Lang.get("menu_runTests"), ICON_TEST) {
@Override
public void actionPerformed(ActionEvent e) {
startTests();
}
}.setToolTip(Lang.get("menu_runTests_tt"));
ToolTipAction speedTest = new ToolTipAction(Lang.get("menu_speedTest")) {
@Override
public void actionPerformed(ActionEvent e) {
try {
Model model = new ModelCreator(circuitComponent.getCircuit(), library).createModel(false);
SpeedTest speedTest = new SpeedTest(model);
double frequency = speedTest.calculate();
circuitComponent.getCircuit().clearState();
JOptionPane.showMessageDialog(Main.this, Lang.get("msg_frequency_N", frequency));
} catch (Exception e1) {
new ErrorMessage("SpeedTestError").addCause(e1).show();
}
}
}.setToolTip(Lang.get("menu_speedTest_tt"));
ToolTipAction editRunAttributes = new ToolTipAction(Lang.get("menu_editRunAttributes")) {
@Override
public void actionPerformed(ActionEvent e) {
new AttributeDialog(Main.this, ATTR_LIST, settings).showDialog();
}
}.setToolTip(Lang.get("menu_editRunAttributes_tt"));
JMenu run = new JMenu(Lang.get("menu_run"));
menuBar.add(run);
run.add(editRunAttributes.createJMenuItem());
run.addSeparator();
run.add(runModelAction.createJMenuItem());
run.add(runModelMicroAction.createJMenuItem());
run.add(doStep.createJMenuItem());
run.add(runToBreakAction.createJMenuItem());
run.add(stoppedStateAction.createJMenuItem());
run.add(runTests.createJMenuItem());
run.addSeparator();
run.add(speedTest.createJMenuItem());
doStep.setEnabled(false);
toolBar.add(runModelState.setIndicator(runModelAction.createJButtonNoText()));
toolBar.add(runToBreakAction.createJButtonNoText());
toolBar.add(stoppedStateAction.createJButtonNoText());
toolBar.addSeparator();
toolBar.add(runModelMicroState.setIndicator(runModelMicroAction.createJButtonNoText()));
toolBar.add(doStep.createJButtonNoText());
toolBar.addSeparator();
toolBar.add(runTests.createJButtonNoText());
}
/**
* starts the tests
*/
public void startTests() {
try {
ArrayList<TestResultDialog.TestSet> tsl = new ArrayList<>();
for (VisualElement el : circuitComponent.getCircuit().getElements())
if (el.equalsDescription(TestCaseElement.TESTCASEDESCRIPTION))
tsl.add(new TestResultDialog.TestSet(
el.getElementAttributes().get(TestCaseElement.TESTDATA),
el.getElementAttributes().getCleanLabel()));
if (tsl.isEmpty())
throw new TestingDataException(Lang.get("err_noTestData"));
windowPosManager.register("testresult", new TestResultDialog(Main.this, tsl, circuitComponent.getCircuit(), library)).setVisible(true);
stoppedState.enter();
} catch (Exception e1) {
showErrorAndStopModel(Lang.get("msg_runningTestError"), e1);
}
}
/**
* Creates the analyse menu
*
* @param menuBar the menu bar
*/
private void createAnalyseMenu(JMenuBar menuBar) {
JMenu analyse = new JMenu(Lang.get("menu_analyse"));
menuBar.add(analyse);
analyse.add(new ToolTipAction(Lang.get("menu_analyse")) {
@Override
public void actionPerformed(ActionEvent e) {
try {
Model model = new ModelCreator(circuitComponent.getCircuit(), library).createModel(false);
new TableDialog(Main.this, new ModelAnalyser(model).analyse(), shapeFactory, filename)
.setVisible(true);
stoppedState.enter();
} catch (PinException | NodeException | AnalyseException | ElementNotFoundException e1) {
showErrorAndStopModel(Lang.get("msg_annalyseErr"), e1);
}
}
}
.setToolTip(Lang.get("menu_analyse_tt"))
.createJMenuItem());
analyse.add(new ToolTipAction(Lang.get("menu_synthesise")) {
@Override
public void actionPerformed(ActionEvent e) {
TruthTable tt = new TruthTable(3).addResult();
new TableDialog(Main.this, tt, shapeFactory, null).setVisible(true);
stoppedState.enter();
}
}
.setToolTip(Lang.get("menu_synthesise_tt"))
.createJMenuItem());
analyse.add(new ToolTipAction(Lang.get("menu_expression")) {
@Override
public void actionPerformed(ActionEvent e) {
new ExpressionDialog(Main.this, shapeFactory).setVisible(true);
}
}
.setToolTip(Lang.get("menu_expression_tt"))
.createJMenuItem());
}
private void orderMeasurements() {
try {
Model m = new ModelCreator(circuitComponent.getCircuit(), library).createModel(false);
stoppedState.enter();
ArrayList<String> names = new ArrayList<>();
for (Signal s : m.getSignals())
names.add(s.getName());
new OrderMerger<String, String>(circuitComponent.getCircuit().getMeasurementOrdering()).order(names);
ElementOrderer.ListOrder<String> o = new ElementOrderer.ListOrder<>(names);
if (new ElementOrderer<>(Main.this, Lang.get("menu_orderMeasurements"), o)
.addOkButton()
.showDialog()) {
circuitComponent.getCircuit().setMeasurementOrdering(names);
}
} catch (Exception e1) {
showErrorAndStopModel(Lang.get("msg_errorCreatingModel"), e1);
}
}
private void setupStates() {
stoppedState = stateManager.register(new State() {
@Override
public void enter() {
super.enter();
clearModelDescription();
circuitComponent.setModeAndReset(false, NoSync.INST);
doStep.setEnabled(false);
stoppedState.getAction().setEnabled(false);
runToBreakAction.setEnabled(false);
}
});
runModelState = stateManager.register(new RunModelState());
runModelMicroState = stateManager.register(new State() {
@Override
public void enter() {
super.enter();
stoppedState.getAction().setEnabled(true);
if (createAndStartModel(false, ModelEvent.MICROSTEP, null))
circuitComponent.setManualChangeObserver(new MicroStepObserver(model));
}
});
}
private void clearModelDescription() {
circuitComponent.removeHighLighted();
if (model != null)
model.close();
modelCreator = null;
model = null;
}
private boolean createAndStartModel(boolean globalRunClock, ModelEvent updateEvent, ModelModifier modelModifier) {
try {
circuitComponent.removeHighLighted();
modelCreator = new ModelCreator(circuitComponent.getCircuit(), library);
if (model != null)
model.close();
model = modelCreator.createModel(true);
statusLabel.setText(Lang.get("msg_N_nodes", model.size()));
realtimeClockRunning = false;
modelSync = null;
if (globalRunClock)
for (Clock c : model.getClocks())
if (c.getFrequency() > 0) {
if (modelSync == null)
modelSync = new LockSync();
model.addObserver(new RealTimeClock(model, c, timerExecuter, this, modelSync, this));
realtimeClockRunning = true;
}
if (modelSync == null)
modelSync = NoSync.INST;
circuitComponent.setModeAndReset(true, modelSync);
if (realtimeClockRunning) {
// if clock is running, enable automatic update of gui
GuiModelObserver gmo = new GuiModelObserver(circuitComponent, updateEvent);
modelCreator.connectToGui(gmo);
model.addObserver(gmo);
} else
// all repainting is initiated by user actions!
modelCreator.connectToGui(null);
doStep.setEnabled(false);
runToBreakAction.setEnabled(!realtimeClockRunning && model.isFastRunModel());
List<String> ordering = circuitComponent.getCircuit().getMeasurementOrdering();
if (settings.get(Keys.SHOW_DATA_TABLE))
windowPosManager.register("probe", new ProbeDialog(this, model, updateEvent, ordering, modelSync)).setVisible(true);
if (settings.get(Keys.SHOW_DATA_GRAPH))
windowPosManager.register("dataset", new DataSetDialog(this, model, updateEvent == ModelEvent.MICROSTEP, ordering, modelSync)).setVisible(true);
if (settings.get(Keys.SHOW_DATA_GRAPH_MICRO))
windowPosManager.register("datasetMicro", new DataSetDialog(this, model, true, ordering, modelSync)).setVisible(true);
if (modelModifier != null)
modelModifier.preInit(model);
model.init();
return true;
} catch (NodeException | PinException | RuntimeException | ElementNotFoundException e) {
showErrorAndStopModel(Lang.get("msg_errorCreatingModel"), e);
}
return false;
}
@Override
public void showErrorAndStopModel(String message, Exception cause) {
SwingUtilities.invokeLater(() -> {
if (cause instanceof NodeException) {
NodeException e = (NodeException) cause;
circuitComponent.addHighLightedWires(e.getValues());
if (modelCreator != null)
modelCreator.addNodeElementsTo(e.getNodes(), circuitComponent.getHighLighted());
} else if (cause instanceof PinException) {
PinException e = (PinException) cause;
circuitComponent.addHighLighted(e.getVisualElement());
if (e.getNet() != null)
circuitComponent.addHighLighted(e.getNet().getWires());
} else if (cause instanceof BurnException) {
BurnException e = (BurnException) cause;
circuitComponent.addHighLightedWires(e.getValues());
}
circuitComponent.hasChanged();
new ErrorMessage(message).addCause(cause).show(Main.this);
stoppedState.enter();
});
}
/**
* stops the model
*/
public void stopModel() {
stoppedState.enter();
}
private static JFileChooser getJFileChooser(File filename) {
File folder = null;
if (filename != null)
folder = filename.getParentFile();
JFileChooser fileChooser = new JFileChooser(folder);
fileChooser.setFileFilter(new FileNameExtensionFilter("Circuit", "dig"));
return fileChooser;
}
@Override
public boolean isStateChanged() {
return circuitComponent.getCircuit().isModified();
}
@Override
public void saveChanges() {
save.actionPerformed(null);
}
@Override
public void open(File file) {
if (ClosingWindowListener.checkForSave(Main.this, Main.this)) {
loadFile(file, true);
}
}
private void loadFile(File filename, boolean toPrefs) {
try {
library.setFilePath(filename.getParentFile());
Circuit circ = Circuit.loadCircuit(filename, shapeFactory);
circuitComponent.setCircuit(circ);
stoppedState.enter();
windowPosManager.closeAll();
setFilename(filename, toPrefs);
statusLabel.setText(" ");
} catch (Exception e) {
circuitComponent.setCircuit(new Circuit());
setFilename(null, false);
new ErrorMessage(Lang.get("msg_errorReadingFile")).addCause(e).show(this);
}
}
private void saveFile(File filename, boolean toPrefs) {
filename = checkSuffix(filename, "dig");
try {
circuitComponent.getCircuit().save(filename);
if (savedListener != null)
savedListener.saved(filename);
stoppedState.enter();
setFilename(filename, toPrefs);
} catch (IOException e) {
new ErrorMessage(Lang.get("msg_errorWritingFile")).addCause(e).show();
}
}
private void setFilename(File filename, boolean toPrefs) {
try {
this.filename = filename;
if (filename != null) {
this.lastFilename = filename;
library.setFilePath(filename.getParentFile());
if (toPrefs)
fileHistory.add(filename);
setTitle(filename + " - " + Lang.get("digital"));
} else {
library.setFilePath(null);
setTitle(Lang.get("digital"));
}
} catch (IOException e) {
new ErrorMessage(Lang.get("msg_errorReadingFile")).addCause(e).show(this);
}
}
/**
* Adds the given suffix to the file
*
* @param filename filename
* @param suffix suffix
* @return the file name with the given suffix
*/
public static File checkSuffix(File filename, String suffix) {
String name = filename.getName();
int p = name.lastIndexOf('.');
if (p >= 0)
name = name.substring(0, p);
return new File(filename.getParentFile(), name + "." + suffix);
}
/**
* @return the window position manager
*/
public WindowPosManager getWindowPosManager() {
return windowPosManager;
}
@Override
public void setStatus(String message) {
SwingUtilities.invokeLater(() -> statusLabel.setText(message));
}
private class FullStepObserver implements Observer {
private final Model model;
FullStepObserver(Model model) {
this.model = model;
}
@Override
public void hasChanged() {
try {
modelSync.accessNEx(() -> {
model.fireManualChangeEvent();
model.doStep();
});
circuitComponent.hasChanged();
} catch (NodeException | RuntimeException e) {
showErrorAndStopModel(Lang.get("msg_errorCalculatingStep"), e);
}
}
}
private class MicroStepObserver implements Observer {
private final Model model;
MicroStepObserver(Model model) {
this.model = model;
}
@Override
public void hasChanged() {
modelCreator.addNodeElementsTo(model.nodesToUpdate(), circuitComponent.getHighLighted());
model.fireManualChangeEvent();
circuitComponent.hasChanged();
doStep.setEnabled(model.needsUpdate());
}
}
private class ExportAction extends ToolTipAction {
private final String name;
private final String suffix;
private final ExportFactory exportFactory;
ExportAction(String name, String suffix, ExportFactory exportFactory) {
super(name);
this.name = name;
this.suffix = suffix;
this.exportFactory = exportFactory;
}
@Override
public void actionPerformed(ActionEvent e) {
JFileChooser fc = new JFileChooser();
if (filename != null)
fc.setSelectedFile(checkSuffix(filename, suffix));
if (lastExportDirectory != null)
fc.setCurrentDirectory(lastExportDirectory);
fc.addChoosableFileFilter(new FileNameExtensionFilter(name, suffix));
if (fc.showSaveDialog(Main.this) == JFileChooser.APPROVE_OPTION) {
lastExportDirectory = fc.getSelectedFile().getParentFile();
try (OutputStream out = new FileOutputStream(checkSuffix(fc.getSelectedFile(), suffix))) {
new Export(circuitComponent.getCircuit(), exportFactory).export(out);
} catch (IOException e1) {
new ErrorMessage(Lang.get("msg_errorWritingFile")).addCause(e1).show(Main.this);
}
}
}
}
private class RunModelState extends State {
@Override
public void enter() {
enter(true, null);
}
void enter(boolean runRealTime, ModelModifier modelModifier) {
super.enter();
stoppedState.getAction().setEnabled(true);
if (createAndStartModel(runRealTime, ModelEvent.STEP, modelModifier))
circuitComponent.setManualChangeObserver(new FullStepObserver(model));
}
}
//***********************
// remote interface start
//***********************
private static class AddressPicker {
private long addr;
private void getProgRomAddr(Model model) {
List<ROM> roms = model.findNode(ROM.class, ROM::isProgramMemory);
if (roms.size() == 1)
addr = roms.get(0).getRomAddress();
else
addr = -1;
}
String getAddrString() {
if (addr < 0)
return null;
else
return Long.toHexString(addr);
}
}
private void setDebug(boolean debug) {
settings.set(Keys.SHOW_DATA_TABLE, debug);
}
@Override
public void start(File romHex) throws RemoteException {
SwingUtilities.invokeLater(() -> {
setDebug(false);
windowPosManager.closeAll();
runModelState.enter(true, new RomLoader(romHex));
circuitComponent.hasChanged();
});
}
@Override
public void debug(File romHex) throws RemoteException {
SwingUtilities.invokeLater(() -> {
setDebug(true);
runModelState.enter(false, new RomLoader(romHex));
circuitComponent.hasChanged();
});
}
@Override
public String doSingleStep() throws RemoteException {
if (model != null && !realtimeClockRunning) {
try {
AddressPicker addressPicker = new AddressPicker();
SwingUtilities.invokeAndWait(() -> {
ArrayList<Clock> cl = model.getClocks();
if (cl.size() == 1) {
ObservableValue clkVal = cl.get(0).getClockOutput();
clkVal.setBool(!clkVal.getBool());
try {
model.doStep();
if (clkVal.getBool()) {
clkVal.setBool(!clkVal.getBool());
model.doStep();
}
circuitComponent.hasChanged();
addressPicker.getProgRomAddr(model);
} catch (NodeException e) {
showErrorAndStopModel(Lang.get("err_remoteExecution"), e);
}
}
});
return addressPicker.getAddrString();
} catch (InterruptedException | InvocationTargetException e) {
throw new RemoteException("error performing a single step " + e.getMessage());
}
}
return null;
}
@Override
public String runToBreak() throws RemoteException {
try {
AddressPicker addressPicker = new AddressPicker();
SwingUtilities.invokeAndWait(() -> {
if (model != null && model.isFastRunModel() && !realtimeClockRunning)
runToBreakAction.actionPerformed(null);
addressPicker.getProgRomAddr(model);
});
return addressPicker.getAddrString();
} catch (InterruptedException | InvocationTargetException e) {
throw new RemoteException("error performing a run to break " + e.getMessage());
}
}
@Override
public void stop() {
SwingUtilities.invokeLater(() -> {
stoppedState.enter();
circuitComponent.hasChanged();
});
}
//**********************
// remote interface end
//**********************
/**
* Starts the main app
*
* @param args the arguments
*/
public static void main(String[] args) {
ToolTipManager.sharedInstance().setDismissDelay(10000);
URL.setURLStreamHandlerFactory(ElementHelpDialog.createURLStreamHandlerFactory());
experimental = args.length == 1 && args[0].equals("experimental");
FormatToExpression.setDefaultFormat(Settings.getInstance().get(Keys.SETTINGS_EXPRESSION_FORMAT));
SwingUtilities.invokeLater(() -> {
Main main = new Main();
try {
new RemoteSever(new DigitalHandler(main)).start(41114);
} catch (IOException e) {
SwingUtilities.invokeLater(() -> main.statusLabel.setText(Lang.get("err_portIsInUse")));
}
main.setVisible(true);
});
}
}