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 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 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 names = new ArrayList<>(); for (Signal s : m.getSignals()) names.add(s.getName()); new OrderMerger(circuitComponent.getCircuit().getMeasurementOrdering()).order(names); ElementOrderer.ListOrder 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 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 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 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); }); } }