diff --git a/src/main/java/de/neemann/digital/builder/ATF1502/CreateCHN.java b/src/main/java/de/neemann/digital/builder/ATF1502/CreateCHN.java new file mode 100644 index 000000000..ce8552adf --- /dev/null +++ b/src/main/java/de/neemann/digital/builder/ATF1502/CreateCHN.java @@ -0,0 +1,28 @@ +package de.neemann.digital.builder.ATF1502; + +import de.neemann.digital.builder.ExpressionToFileExporter; +import de.neemann.digital.gui.Main; + +import java.io.*; + +/** + * Created by hneemann on 10.03.17. + */ +public class CreateCHN implements ExpressionToFileExporter.PostProcess { + @Override + public File execute(File file) throws IOException { + File chnFile = Main.checkSuffix(file, "chn"); + System.out.println("create chn from " + file); + + try (Writer chn = new OutputStreamWriter(new FileOutputStream(chnFile), "UTF-8")) { + chn.write("1 4 1 0 \r\n" + + "\r\n" + + "ATF1502AS\r\n" + + "10\r\n" + + "1\r\n"); + chn.write(chnFile.getPath()); + } + + return chnFile; + } +} diff --git a/src/main/java/de/neemann/digital/builder/ExpressionToFileExporter.java b/src/main/java/de/neemann/digital/builder/ExpressionToFileExporter.java new file mode 100644 index 000000000..4c12736bf --- /dev/null +++ b/src/main/java/de/neemann/digital/builder/ExpressionToFileExporter.java @@ -0,0 +1,100 @@ +package de.neemann.digital.builder; + +import de.neemann.digital.builder.jedec.FuseMapFillerException; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; + +/** + * Used to create a output chain of files created + * Created by hneemann on 10.03.17. + */ +public class ExpressionToFileExporter { + + private final ExpressionExporter exporter; + private final ArrayList postProcesses; + + /** + * Creates a new instance + * + * @param exporter the initial export to create the initial file + */ + public ExpressionToFileExporter(ExpressionExporter exporter) { + this.exporter = exporter; + postProcesses = new ArrayList<>(); + } + + /** + * @return the initial exporter + */ + public ExpressionExporter getExporter() { + return exporter; + } + + /** + * Is delegated to exporter.getPinMap. + * + * @return the pin map + */ + public PinMap getPinMapping() { + return exporter.getPinMapping(); + } + + /** + * Is delegated to exporter.getBuilder. + * + * @return the builder + */ + public BuilderInterface getBuilder() { + return exporter.getBuilder(); + } + + /** + * Adds a processing step. + * All steps are executed after the initial fals has been created. + * + * @param postProcess the process to start + * @return this for chained calls + */ + public ExpressionToFileExporter addProcessingStep(PostProcess postProcess) { + postProcesses.add(postProcess); + return this; + } + + /** + * Runs the export chain + * + * @param file the name of the initial file + * @throws IOException IOException + * @throws PinMapException PinMapException + * @throws FuseMapFillerException FuseMapFillerException + */ + public void export(File file) throws IOException, PinMapException, FuseMapFillerException { + try (OutputStream out = new FileOutputStream(file)) { + exporter.writeTo(out); + } + for (PostProcess p : postProcesses) + try { + file = p.execute(file); + } catch (IOException e) { + throw new IOException("post process error in " + p.toString(), e); + } + } + + /** + * PostProcess is used to start further steps creating the final output file + */ + public interface PostProcess { + /** + * Execute a new process + * + * @param file the file to process + * @return the new file created or, if no file is created the given file is returned + * @throws IOException IOException + */ + File execute(File file) throws IOException; + } +} diff --git a/src/main/java/de/neemann/digital/builder/tt2/OSExecute.java b/src/main/java/de/neemann/digital/builder/tt2/OSExecute.java new file mode 100644 index 000000000..a255ab888 --- /dev/null +++ b/src/main/java/de/neemann/digital/builder/tt2/OSExecute.java @@ -0,0 +1,127 @@ +package de.neemann.digital.builder.tt2; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * OSExecute is used to start external programs + * It is used to start external fitters like fit1502.exe + *

+ * Created by hneemann on 10.03.17. + */ +public class OSExecute { + + private final ProcessBuilder procesBuilder; + private File workingDir; + + /** + * Creates a new instance + * + * @param args the program to start + */ + public OSExecute(String... args) { + procesBuilder = new ProcessBuilder(args); + } + + /** + * Creates a new instance + * + * @param args the program to start + */ + public OSExecute(List args) { + procesBuilder = new ProcessBuilder(args); + } + + /** + * Sets the working directory + * + * @param workingDir the working directory + */ + public void setWorkingDir(File workingDir) { + this.workingDir = workingDir; + } + + /** + * Starts the execution and waits for its completion. + * + * @return the console output of the of the started process + * @throws IOException IOException + */ + public String start() throws IOException { + if (workingDir != null) + procesBuilder.directory(workingDir); + + + procesBuilder.redirectErrorStream(true); + + Process p = procesBuilder.start(); + + InputStream console = p.getInputStream(); + StreamReader sr = new StreamReader(console); + sr.start(); + + try { + p.waitFor(30, TimeUnit.SECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + if (p.isAlive()) { + p.destroy(); + sr.interrupt(); + throw new IOException("Process does not terminate!"); + } + + if (p.exitValue() != 0) + throw new IOException("got non zero exit value " + p.exitValue()); + + try { + sr.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + if (sr.getException() != null) + throw sr.getException(); + + return sr.toString(); + } + + private static final class StreamReader extends Thread { + private final InputStream console; + private final ByteArrayOutputStream baos; + private IOException exception; + + private StreamReader(InputStream console) { + this.console = console; + baos = new ByteArrayOutputStream(); + setDaemon(true); + } + + @Override + public void run() { + try { + byte[] data = new byte[4096]; + int l; + while ((l = console.read(data)) >= 0) { + baos.write(data, 0, l); + } + } catch (IOException e) { + exception = e; + } + } + + private IOException getException() { + return exception; + } + + @Override + public String toString() { + return baos.toString(); + } + } +} diff --git a/src/main/java/de/neemann/digital/builder/tt2/StartFitter.java b/src/main/java/de/neemann/digital/builder/tt2/StartFitter.java new file mode 100644 index 000000000..dfcd46e8a --- /dev/null +++ b/src/main/java/de/neemann/digital/builder/tt2/StartFitter.java @@ -0,0 +1,59 @@ +package de.neemann.digital.builder.tt2; + +import de.neemann.digital.builder.ExpressionToFileExporter; +import de.neemann.digital.core.element.Keys; +import de.neemann.digital.gui.Main; +import de.neemann.digital.gui.Settings; + +import javax.swing.*; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; + +/** + * Starts a fitter to create a JEDEC file. + * Created by hneemann on 10.03.17. + */ +public class StartFitter implements ExpressionToFileExporter.PostProcess { + private final JDialog parent; + private final File fitterExe; + + /** + * Creates a new instance + * + * @param parent the parent dialog + */ + public StartFitter(JDialog parent) { + this.parent = parent; + this.fitterExe = Settings.getInstance().get(Keys.SETTINGS_ATF1502_FITTER); + } + + @Override + public File execute(File file) throws IOException { + ArrayList args = new ArrayList<>(); + + if (isLinux()) + args.add("wine"); + args.add(fitterExe.toString()); + args.add(file.getName()); + + OSExecute execute = new OSExecute(args); + execute.setWorkingDir(file.getParentFile()); + + String message = execute.start(); + + SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(parent, message)); + + return Main.checkSuffix(file, "jed"); + } + + private boolean isLinux() { + String name = System.getProperty("os.name").toLowerCase(); + return name.contains("linux"); + } + + @Override + public String toString() { + return "External fitter to create a JEDEC file"; + } +} diff --git a/src/main/java/de/neemann/digital/core/element/Keys.java b/src/main/java/de/neemann/digital/core/element/Keys.java index 6748a1eb8..758ac75b3 100644 --- a/src/main/java/de/neemann/digital/core/element/Keys.java +++ b/src/main/java/de/neemann/digital/core/element/Keys.java @@ -6,6 +6,7 @@ import de.neemann.digital.core.memory.DataField; import de.neemann.gui.language.Language; import java.awt.*; +import java.io.File; import java.util.Locale; /** @@ -295,4 +296,10 @@ public final class Keys { public static final Key LED_PERSISTENCE = new Key<>("ledPersistence", false); + /** + * Fitter for the atf1502 + */ + public static final Key SETTINGS_ATF1502_FITTER + = new Key<>("atf1502Fitter", new File("fit1502.exe")); + } diff --git a/src/main/java/de/neemann/digital/gui/Settings.java b/src/main/java/de/neemann/digital/gui/Settings.java index 39bff3d3c..8571b9a20 100644 --- a/src/main/java/de/neemann/digital/gui/Settings.java +++ b/src/main/java/de/neemann/digital/gui/Settings.java @@ -32,6 +32,7 @@ public final class Settings implements AttributeListener { INT_LIST.add(Keys.SETTINGS_IEEE_SHAPES); INT_LIST.add(Keys.SETTINGS_LANGUAGE); INT_LIST.add(Keys.SETTINGS_EXPRESSION_FORMAT); + INT_LIST.add(Keys.SETTINGS_ATF1502_FITTER); } private static final class SettingsHolder { diff --git a/src/main/java/de/neemann/digital/gui/components/EditorFactory.java b/src/main/java/de/neemann/digital/gui/components/EditorFactory.java index cba5df9c9..16946d308 100644 --- a/src/main/java/de/neemann/digital/gui/components/EditorFactory.java +++ b/src/main/java/de/neemann/digital/gui/components/EditorFactory.java @@ -23,6 +23,7 @@ import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.text.JTextComponent; import java.awt.*; import java.awt.event.ActionEvent; +import java.io.File; import java.io.IOException; import java.lang.reflect.Constructor; import java.util.HashMap; @@ -42,6 +43,7 @@ public final class EditorFactory { private EditorFactory() { add(String.class, StringEditor.class); add(Integer.class, IntegerEditor.class); + add(File.class, FileEditor.class); add(Color.class, ColorEditor.class); add(Boolean.class, BooleanEditor.class); add(DataField.class, DataFieldEditor.class); @@ -251,6 +253,37 @@ public final class EditorFactory { } } + private final static class FileEditor extends LabelEditor { + + private final JPanel panel; + private final JTextField textField; + + public FileEditor(File value, Key key) { + panel = new JPanel(new BorderLayout()); + textField = new JTextField(value.getPath(), 20); + JButton button = new JButton(new AbstractAction("...") { + @Override + public void actionPerformed(ActionEvent e) { + JFileChooser fc = new JFileChooser(FileEditor.this.getValue()); + if (fc.showOpenDialog(panel) == JFileChooser.APPROVE_OPTION) + textField.setText(fc.getSelectedFile().getPath()); + } + }); + panel.add(textField, BorderLayout.CENTER); + panel.add(button, BorderLayout.EAST); + } + + @Override + public JComponent getComponent(ElementAttributes attr) { + return panel; + } + + @Override + public File getValue() { + return new File(textField.getText()); + } + } + private final static class DataFieldEditor extends LabelEditor { private DataField data; diff --git a/src/main/java/de/neemann/digital/gui/components/table/TableDialog.java b/src/main/java/de/neemann/digital/gui/components/table/TableDialog.java index 7da87c33b..f682083fe 100644 --- a/src/main/java/de/neemann/digital/gui/components/table/TableDialog.java +++ b/src/main/java/de/neemann/digital/gui/components/table/TableDialog.java @@ -16,6 +16,7 @@ import de.neemann.digital.analyse.format.TruthTableFormatterLaTeX; import de.neemann.digital.analyse.quinemc.BoolTableIntArray; import de.neemann.digital.builder.ATF1502.ATF1502CuplExporter; import de.neemann.digital.builder.ATF1502.ATF1502TT2Exporter; +import de.neemann.digital.builder.ATF1502.CreateCHN; import de.neemann.digital.builder.*; import de.neemann.digital.builder.Gal16v8.Gal16v8CuplExporter; import de.neemann.digital.builder.Gal16v8.Gal16v8JEDECExporter; @@ -23,6 +24,7 @@ import de.neemann.digital.builder.Gal22v10.Gal22v10CuplExporter; import de.neemann.digital.builder.Gal22v10.Gal22v10JEDECExporter; import de.neemann.digital.builder.circuit.CircuitBuilder; import de.neemann.digital.builder.jedec.FuseMapFillerException; +import de.neemann.digital.builder.tt2.StartFitter; import de.neemann.digital.draw.elements.Circuit; import de.neemann.digital.draw.shapes.ShapeFactory; import de.neemann.digital.gui.Main; @@ -47,7 +49,6 @@ import java.awt.event.MouseEvent; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; -import java.io.OutputStream; import java.util.ArrayList; import java.util.HashSet; @@ -402,7 +403,7 @@ public class TableDialog extends JDialog { @Override public void actionPerformed(ActionEvent actionEvent) { Gal16v8JEDECExporter jedecExporter = new Gal16v8JEDECExporter(); - createHardware(jedecExporter, filename, "jed"); + createHardware(new ExpressionToFileExporter(jedecExporter), filename, "jed"); new ShowStringDialog(parent, Lang.get("win_pinMapDialog"), jedecExporter.getPinMapping().toString()).setVisible(true); } }.setToolTip(Lang.get("menu_table_create_jedec_tt")).createJMenuItem()); @@ -419,7 +420,7 @@ public class TableDialog extends JDialog { @Override public void actionPerformed(ActionEvent actionEvent) { Gal22v10JEDECExporter jedecExporter = new Gal22v10JEDECExporter(); - createHardware(jedecExporter, filename, "jed"); + createHardware(new ExpressionToFileExporter(jedecExporter), filename, "jed"); new ShowStringDialog(parent, Lang.get("win_pinMapDialog"), jedecExporter.getPinMapping().toString()).setVisible(true); } }.setToolTip(Lang.get("menu_table_create_jedec_tt")).createJMenuItem()); @@ -436,7 +437,10 @@ public class TableDialog extends JDialog { atf1502.add(new ToolTipAction(Lang.get("menu_table_createTT2")) { @Override public void actionPerformed(ActionEvent actionEvent) { - createHardware(new ATF1502TT2Exporter(), filename, "tt2"); + createHardware( + new ExpressionToFileExporter(new ATF1502TT2Exporter()) + .addProcessingStep(new StartFitter(TableDialog.this)) + .addProcessingStep(new CreateCHN()), filename, "tt2"); } }.setToolTip(Lang.get("menu_table_createTT2_tt")).createJMenuItem()); hardware.add(atf1502); @@ -447,7 +451,7 @@ public class TableDialog extends JDialog { return createMenu; } - private void createHardware(ExpressionExporter expressionExporter, File filename, String suffix) { + private void createHardware(ExpressionToFileExporter expressionExporter, File filename, String suffix) { if (filename == null) filename = new File("circuit." + suffix); else @@ -458,11 +462,9 @@ public class TableDialog extends JDialog { fileChooser.setSelectedFile(filename); if (fileChooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) { try { - try (OutputStream out = new FileOutputStream(Main.checkSuffix(fileChooser.getSelectedFile(), suffix))) { - expressionExporter.getPinMapping().addAll(pinMap); - new BuilderExpressionCreator(expressionExporter.getBuilder(), ExpressionModifier.IDENTITY).create(); - expressionExporter.writeTo(out); - } + expressionExporter.getPinMapping().addAll(pinMap); + new BuilderExpressionCreator(expressionExporter.getBuilder(), ExpressionModifier.IDENTITY).create(); + expressionExporter.export(Main.checkSuffix(fileChooser.getSelectedFile(), suffix)); } catch (ExpressionException | FormatterException | IOException | FuseMapFillerException | PinMapException e) { new ErrorMessage(Lang.get("msg_errorDuringCalculation")).addCause(e).show(this); } diff --git a/src/main/resources/lang/lang_de.xml b/src/main/resources/lang/lang_de.xml index 1279b6d46..76b117077 100644 --- a/src/main/resources/lang/lang_de.xml +++ b/src/main/resources/lang/lang_de.xml @@ -524,6 +524,8 @@ Zur Analyse können Sie die Schaltung im Gatterschrittmodus ausführen. Die Schaltfrequenz in der Simulation kann nicht so hoch werden, dass das menschliche Auge kein Flimmern mehr wahrnimmt. Um dennoch das Flackern zu unterdrücken, kann bei den LEDs mit dieser Option ein "nachleuchten" eingeschaltet werden. + ATF1502 Fitter + Pfad zum Fitter für den ATF1502. Geben Sie hier den vollen Pfad zur Datei fit1502.exe an. Diese Datei wird von ATMEL zu Verfügung gestellt. Logisch Arithmetik diff --git a/src/main/resources/lang/lang_en.xml b/src/main/resources/lang/lang_en.xml index 7f5ab5dd7..1c3fd9e8a 100644 --- a/src/main/resources/lang/lang_en.xml +++ b/src/main/resources/lang/lang_en.xml @@ -509,8 +509,9 @@ To analyse you can run the circuit in single gate step mode. Avoid Flicker It is not possible to increase the frequency so much that the flickering disappears. With this option you can stabilize the display by keeping the LEDs on until the common cathode goes down again. - This simulates a frequency above the critical flicker fusion frequency. - + This simulates a frequency above the critical flicker fusion frequency. + ATF1502 Fitter + Path to the fitter for the ATF1502. Enter the full path to the file fit1502.exe provided bei ATMEL. Logic Arithmetic