mirror of
https://github.com/hneemann/Digital.git
synced 2025-09-14 07:17:13 -04:00
added a first trace window
This commit is contained in:
parent
dc364b89c7
commit
95d4d3ecf1
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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
|
||||
|
||||
|
@ -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?
|
||||
|
Loading…
x
Reference in New Issue
Block a user