added a first trace window

This commit is contained in:
hneemann 2016-04-03 18:27:59 +02:00
parent dc364b89c7
commit 95d4d3ecf1
10 changed files with 300 additions and 9 deletions

View File

@ -55,6 +55,7 @@ public class Model {
private ArrayList<Node> nodesToUpdateNext;
private int version;
private boolean isInitialized = false;
private boolean signalsOrdered = false;
/**
* Creates a new model
@ -83,9 +84,8 @@ public class Model {
/**
* Adds a node to the model
*
* @param node the node
* @param <T> type of the node
* @param <T> type of the node
* @return the node itself for chained calls
*/
public <T extends Node> T add(T node) {
@ -324,6 +324,10 @@ public class Model {
}
public ArrayList<Signal> getSignals() {
if (!signalsOrdered) {
Collections.sort(signals);
signalsOrdered = true;
}
return signals;
}
@ -335,6 +339,10 @@ public class Model {
return roms;
}
public void fireManualChangeEvent() {
fireEvent(ModelEvent.MANUALCHANGE);
}
public static class Signal implements Comparable<Signal> {
private final String name;

View File

@ -10,8 +10,9 @@ public class ModelEvent {
public static final ModelEvent STARTED = new ModelEvent(Event.STARTED);
public static final ModelEvent BREAK = new ModelEvent(Event.BREAK);
public static final ModelEvent STOPPED = new ModelEvent(Event.STOPPED);
public static final ModelEvent MANUALCHANGE = new ModelEvent(Event.MANUALCHANGE);
public enum Event {STARTED, STOPPED, STEP, BREAK, MICROSTEP}
public enum Event {STARTED, STOPPED, STEP, BREAK, MANUALCHANGE, MICROSTEP}
private final Event event;

View File

@ -14,6 +14,7 @@ import de.neemann.digital.draw.shapes.ShapeFactory;
import de.neemann.digital.gui.components.CircuitComponent;
import de.neemann.digital.gui.components.ElementOrderer;
import de.neemann.digital.gui.components.ProbeDialog;
import de.neemann.digital.gui.components.data.DataSetDialog;
import de.neemann.digital.gui.components.listing.ROMListingDialog;
import de.neemann.digital.gui.state.State;
import de.neemann.digital.gui.state.StateManager;
@ -57,8 +58,8 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave {
private final ElementLibrary library;
private final JCheckBoxMenuItem runClock;
private final JCheckBoxMenuItem showProbes;
private final JCheckBoxMenuItem showGraph;
private final JCheckBoxMenuItem showListing;
private final JCheckBoxMenuItem traceEnable;
private final LibrarySelector librarySelector;
private final ShapeFactory shapeFactory;
private final SavedListener savedListener;
@ -288,7 +289,8 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave {
showListing.setToolTipText(Lang.get("menu_listing_tt"));
showProbes = new JCheckBoxMenuItem(Lang.get("menu_probe"));
showProbes.setToolTipText(Lang.get("menu_probe_tt"));
traceEnable = new JCheckBoxMenuItem(Lang.get("menu_trace"));
showGraph = new JCheckBoxMenuItem(Lang.get("menu_graph"));
showGraph.setToolTipText(Lang.get("menu_graph_tt"));
runClock = new JCheckBoxMenuItem(Lang.get("menu_runClock"));
runClock.setToolTipText(Lang.get("menu_runClock_tt"));
@ -298,8 +300,8 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave {
run.add(runToBreak.createJMenuItem());
//run.add(speedTest.createJMenuItem());
run.add(showProbes);
run.add(showGraph);
run.add(showListing);
//run.add(traceEnable);
run.add(runClock);
doStep.setEnabled(false);
@ -404,6 +406,10 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave {
if (showProbes.isSelected())
new ProbeDialog(this, model, updateEvent).setVisible(true);
if (showGraph.isSelected())
new DataSetDialog(this, model, updateEvent).setVisible(true);
if (showListing.isSelected())
for (ROM rom : model.getRoms())
try {
@ -538,6 +544,7 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave {
@Override
public void hasChanged() {
modelDescription.addNodeElementsTo(model.nodesToUpdate(), circuitComponent.getHighLighted());
model.fireManualChangeEvent();
circuitComponent.repaint();
doStep.setEnabled(model.needsUpdate());
}

View File

@ -13,7 +13,6 @@ import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Collections;
/**
* @author hneemann
@ -29,7 +28,6 @@ public class ProbeDialog extends JDialog implements ModelStateObserver {
this.type = type;
ArrayList<Model.Signal> signals = model.getSignals();
Collections.sort(signals);
tableModel = new SignalTableModel(signals);
JTable list = new JTable(tableModel);
getContentPane().add(new JScrollPane(list), BorderLayout.CENTER);
@ -55,7 +53,7 @@ public class ProbeDialog extends JDialog implements ModelStateObserver {
@Override
public void handleEvent(ModelEvent event) {
if (event.getType() == type) {
if (event.getType() == type || event.getType() == ModelEvent.Event.MANUALCHANGE) {
tableModel.fireChanged();
}
}

View File

@ -0,0 +1,42 @@
package de.neemann.digital.gui.components.data;
import de.neemann.digital.core.Model;
import java.util.ArrayList;
/**
* @author hneemann
*/
public class DataSample {
private final int mainTime;
private final long[] values;
public DataSample(int mainTime, int valueCount) {
this.mainTime = mainTime;
values = new long[valueCount];
}
public DataSample(DataSample sample) {
this(sample.mainTime, sample.values.length);
System.arraycopy(sample.values, 0, values, 0, values.length);
}
public int getMainTime() {
return mainTime;
}
public long getValue(int i) {
return values[i];
}
public void setValue(int i, long value) {
values[i] = value;
}
public DataSample fillWith(ArrayList<Model.Signal> signals) {
for (int i = 0; i < signals.size(); i++)
values[i] = signals.get(i).getValue().getValueIgnoreBurn();
return this;
}
}

View File

@ -0,0 +1,68 @@
package de.neemann.digital.gui.components.data;
import de.neemann.digital.core.Model;
import java.util.ArrayList;
import java.util.Iterator;
/**
* @author hneemann
*/
public class DataSet implements Iterable<DataSample> {
private static final int MAX_SAMPLES = 1000;
private final ArrayList<Model.Signal> signals;
private final ArrayList<DataSample> samples;
private DataSample min;
private DataSample max;
public DataSet(ArrayList<Model.Signal> signals) {
this.signals = signals;
samples = new ArrayList<>();
}
public void add(DataSample sample) {
if (samples.size() < MAX_SAMPLES) {
samples.add(sample);
if (min == null) {
min = new DataSample(sample);
max = new DataSample(sample);
} else {
for (int i = 0; i < signals.size(); i++) {
if (sample.getValue(i) < min.getValue(i))
min.setValue(i, sample.getValue(i));
if (sample.getValue(i) > max.getValue(i))
max.setValue(i, sample.getValue(i));
}
}
}
}
public int size() {
return samples.size();
}
public int signalSize() {
return signals.size();
}
@Override
public Iterator<DataSample> iterator() {
return samples.iterator();
}
public DataSample getMin() {
return min;
}
public DataSample getMax() {
return max;
}
public long getWidth(int i) {
return max.getValue(i) - min.getValue(i);
}
public Model.Signal getSignal(int i) {
return signals.get(i);
}
}

View File

@ -0,0 +1,87 @@
package de.neemann.digital.gui.components.data;
import javax.swing.*;
import java.awt.*;
/**
* @author hneemann
*/
public class DataSetComponent extends JComponent {
private static final int BORDER = 10;
private static final int SIZE = 20;
private static final int SEP2 = 3;
private static final int SEP = SEP2 * 2;
private static final Stroke NORMAL = new BasicStroke(0);
private static final Stroke THICK = new BasicStroke(2);
private final DataSet dataSet;
private int textWidth;
public DataSetComponent(DataSet dataSet) {
this.dataSet = dataSet;
}
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
textWidth = 0;
for (int i = 0; i < dataSet.signalSize(); i++) {
String text = dataSet.getSignal(i).getName();
int w = g.getFontMetrics().stringWidth(text);
if (w > textWidth) textWidth = w;
}
int x = textWidth + BORDER + SEP;
int yOffs = SIZE / 2 + g.getFontMetrics().getHeight() / 2;
g.setColor(Color.BLACK);
int y = BORDER;
for (int i = 0; i < dataSet.signalSize(); i++) {
String text = dataSet.getSignal(i).getName();
g2.setColor(Color.BLACK);
g.drawString(text, BORDER, y + yOffs);
g2.setColor(Color.LIGHT_GRAY);
g.drawLine(x, y - SEP2, x + SIZE * dataSet.size(), y - SEP2);
y += SIZE + SEP;
}
g.drawLine(x, y - SEP2, x + SIZE * dataSet.size(), y - SEP2);
int[] last_ry = new int[dataSet.signalSize()];
boolean first = true;
for (DataSample s : dataSet) {
g2.setStroke(NORMAL);
g2.setColor(Color.LIGHT_GRAY);
g.drawLine(x, BORDER - SEP2, x, (SIZE + SEP) * dataSet.signalSize() + BORDER - SEP2);
g2.setStroke(THICK);
g2.setColor(Color.BLACK);
y = BORDER;
for (int i = 0; i < dataSet.signalSize(); i++) {
long width = dataSet.getWidth(i);
if (width == 0) width = 1;
//int ry = (int) (SIZE-(SIZE*(s.getValue(i)-dataSet.getMin().getValue(i)))/ width);
int ry = (int) (SIZE - (SIZE * s.getValue(i)) / width);
g.drawLine(x, y + ry, x + SIZE, y + ry);
if (!first && ry != last_ry[i])
g.drawLine(x, y + last_ry[i], x, y + ry);
last_ry[i] = ry;
y += SIZE + SEP;
}
first = false;
x += SIZE;
}
g2.setStroke(NORMAL);
g2.setColor(Color.LIGHT_GRAY);
g.drawLine(x, BORDER - SEP2, x, (SIZE + SEP) * dataSet.signalSize() + BORDER - SEP2);
}
@Override
public Dimension getPreferredSize() {
int count = dataSet.size();
if (count < 10) count = 10;
return new Dimension(SIZE * count + BORDER * 2 + textWidth, (SIZE + SEP) * dataSet.signalSize() + BORDER * 2);
}
}

View File

@ -0,0 +1,76 @@
package de.neemann.digital.gui.components.data;
import de.neemann.digital.core.Model;
import de.neemann.digital.core.ModelEvent;
import de.neemann.digital.core.ModelStateObserver;
import de.neemann.digital.lang.Lang;
import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
/**
* @author hneemann
*/
public class DataSetDialog extends JDialog implements ModelStateObserver {
private final ModelEvent.Event type;
private final ArrayList<Model.Signal> signals;
private final DataSetComponent dsc;
private DataSample manualSample;
private int maintime;
private DataSet dataSet;
public DataSetDialog(Frame owner, Model model, ModelEvent.Event type) {
super(owner, Lang.get("win_measures"), false);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setAlwaysOnTop(true);
this.type = type;
signals = model.getSignals();
dataSet = new DataSet(signals);
dsc = new DataSetComponent(dataSet);
JScrollPane scrollPane = new JScrollPane(dsc);
getContentPane().add(scrollPane);
addWindowListener(new WindowAdapter() {
@Override
public void windowOpened(WindowEvent e) {
model.addObserver(DataSetDialog.this);
}
@Override
public void windowClosed(WindowEvent e) {
model.removeObserver(DataSetDialog.this);
}
});
scrollPane.getViewport().setPreferredSize(dsc.getPreferredSize());
pack();
setLocationRelativeTo(owner);
}
@Override
public void handleEvent(ModelEvent event) {
if (event.getType() == ModelEvent.Event.MANUALCHANGE) {
if (manualSample == null)
manualSample = new DataSample(maintime, signals.size());
manualSample.fillWith(signals);
}
if (event.getType() == type) {
if (manualSample != null) {
dataSet.add(manualSample);
manualSample = null;
maintime++;
}
dataSet.add(new DataSample(maintime, signals.size()).fillWith(signals));
maintime++;
}
dsc.repaint();
}
}

View File

@ -144,6 +144,8 @@ menu_probe=Zeige Messwerte
menu_probe_tt=Zeigt die Messwerte in einem eigenen Fenster an.
menu_listing=Zeige Listing
menu_listing_tt=Zeigt ein ROM-Listing mit der aktuellen Adresse markiert in eine eigenen Fenster an.
menu_graph=Messwerte grafisch darstellen
menu_graph_tt=Zeigt eine Grafik mit dem Messwerten \u00FCber der Zeit.
menu_about=\u00DCber Digital

View File

@ -144,6 +144,8 @@ menu_probe=Show Probe Values
menu_probe_tt=Shows values of probes in a separate window
menu_listing=Show Listing
menu_listing_tt=Shows a ROM listing with actual position marked in a separate window
menu_graph=Show Graph
menu_graph_tt=Shows a graph containing the measurement values
win_saveChanges=Save Changes?