mirror of
https://github.com/hneemann/Digital.git
synced 2025-09-10 13:26:43 -04:00
adds a triggered scope, closes #571
This commit is contained in:
parent
fa42431b05
commit
ac0d9db8be
@ -27,6 +27,7 @@ import de.neemann.digital.draw.elements.Tunnel;
|
|||||||
import de.neemann.digital.draw.shapes.ShapeFactory;
|
import de.neemann.digital.draw.shapes.ShapeFactory;
|
||||||
import de.neemann.digital.gui.Settings;
|
import de.neemann.digital.gui.Settings;
|
||||||
import de.neemann.digital.gui.components.data.DummyElement;
|
import de.neemann.digital.gui.components.data.DummyElement;
|
||||||
|
import de.neemann.digital.gui.components.data.ScopeTrigger;
|
||||||
import de.neemann.digital.gui.components.graphics.GraphicCard;
|
import de.neemann.digital.gui.components.graphics.GraphicCard;
|
||||||
import de.neemann.digital.gui.components.graphics.LedMatrix;
|
import de.neemann.digital.gui.components.graphics.LedMatrix;
|
||||||
import de.neemann.digital.gui.components.graphics.VGA;
|
import de.neemann.digital.gui.components.graphics.VGA;
|
||||||
@ -130,6 +131,7 @@ public class ElementLibrary implements Iterable<ElementLibrary.ElementContainer>
|
|||||||
.add(DummyElement.TEXTDESCRIPTION)
|
.add(DummyElement.TEXTDESCRIPTION)
|
||||||
.add(Probe.DESCRIPTION)
|
.add(Probe.DESCRIPTION)
|
||||||
.add(DummyElement.DATADESCRIPTION)
|
.add(DummyElement.DATADESCRIPTION)
|
||||||
|
.add(ScopeTrigger.DESCRIPTION)
|
||||||
.add(new LibraryNode(Lang.get("lib_displays"))
|
.add(new LibraryNode(Lang.get("lib_displays"))
|
||||||
.add(RGBLED.DESCRIPTION)
|
.add(RGBLED.DESCRIPTION)
|
||||||
.add(Out.POLARITYAWARELEDDESCRIPTION)
|
.add(Out.POLARITYAWARELEDDESCRIPTION)
|
||||||
|
@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Helmut Neemann.
|
||||||
|
* Use of this source code is governed by the GPL v3 license
|
||||||
|
* that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
package de.neemann.digital.gui.components.data;
|
||||||
|
|
||||||
|
import de.neemann.digital.core.*;
|
||||||
|
import de.neemann.digital.core.element.Element;
|
||||||
|
import de.neemann.digital.core.element.ElementAttributes;
|
||||||
|
import de.neemann.digital.core.element.ElementTypeDescription;
|
||||||
|
import de.neemann.digital.core.element.Keys;
|
||||||
|
import de.neemann.digital.data.Value;
|
||||||
|
import de.neemann.digital.data.ValueTable;
|
||||||
|
import de.neemann.digital.draw.elements.PinException;
|
||||||
|
import de.neemann.digital.gui.Main;
|
||||||
|
import de.neemann.digital.gui.components.OrderMerger;
|
||||||
|
import de.neemann.digital.lang.Lang;
|
||||||
|
import de.neemann.digital.testing.parser.TestRow;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.event.WindowAdapter;
|
||||||
|
import java.awt.event.WindowEvent;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static de.neemann.digital.core.element.PinInfo.input;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ScopeElement
|
||||||
|
*/
|
||||||
|
public class ScopeTrigger extends Node implements Element {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ScopeElement description
|
||||||
|
*/
|
||||||
|
public static final ElementTypeDescription DESCRIPTION =
|
||||||
|
new ElementTypeDescription(ScopeTrigger.class, input("T").setClock())
|
||||||
|
.addAttribute(Keys.LABEL)
|
||||||
|
.addAttribute(Keys.MAX_STEP_COUNT);
|
||||||
|
|
||||||
|
private final int maxSize;
|
||||||
|
private final String label;
|
||||||
|
private ObservableValue clockValue;
|
||||||
|
private boolean lastClock;
|
||||||
|
private ValueTable logData;
|
||||||
|
private ArrayList<Signal> signals;
|
||||||
|
private Model model;
|
||||||
|
private GraphDialog graphDialog;
|
||||||
|
private boolean clockHasChanged;
|
||||||
|
private ScopeModelStateObserver scopeModelStateObserver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance
|
||||||
|
*
|
||||||
|
* @param attr the elements attributes
|
||||||
|
*/
|
||||||
|
public ScopeTrigger(ElementAttributes attr) {
|
||||||
|
label = attr.getLabel();
|
||||||
|
maxSize = attr.get(Keys.MAX_STEP_COUNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setInputs(ObservableValues inputs) throws NodeException {
|
||||||
|
clockValue = inputs.get(0).checkBits(1, this).addObserverToValue(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void readInputs() throws NodeException {
|
||||||
|
boolean clock = clockValue.getBool();
|
||||||
|
if (clock != lastClock)
|
||||||
|
clockHasChanged = true;
|
||||||
|
lastClock = clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeOutputs() throws NodeException {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ObservableValues getOutputs() throws PinException {
|
||||||
|
return ObservableValues.EMPTY_LIST;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Model model) throws NodeException {
|
||||||
|
signals = model.getSignalsCopy();
|
||||||
|
this.model = model;
|
||||||
|
signals.removeIf(signal -> !signal.isShowInGraph());
|
||||||
|
|
||||||
|
String[] names = new String[signals.size()];
|
||||||
|
for (int i = 0; i < signals.size(); i++)
|
||||||
|
names[i] = signals.get(i).getName();
|
||||||
|
|
||||||
|
JFrame m = model.getWindowPosManager().getMainFrame();
|
||||||
|
if (m instanceof Main) {
|
||||||
|
List<String> ordering = ((Main) m).getCircuitComponent().getCircuit().getMeasurementOrdering();
|
||||||
|
new OrderMerger<String, Signal>(ordering) {
|
||||||
|
@Override
|
||||||
|
public boolean equals(Signal a, String b) {
|
||||||
|
return a.getName().equals(b);
|
||||||
|
}
|
||||||
|
}.order(signals);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logData = new ValueTable(names).setMaxSize(maxSize);
|
||||||
|
|
||||||
|
scopeModelStateObserver = new ScopeModelStateObserver();
|
||||||
|
model.addObserver(scopeModelStateObserver, ModelEventType.STEP);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class ScopeModelStateObserver implements ModelStateObserver {
|
||||||
|
@Override
|
||||||
|
public void handleEvent(ModelEvent event) {
|
||||||
|
if (clockHasChanged && event.getType() == ModelEventType.STEP) {
|
||||||
|
Value[] sample = new Value[logData.getColumns()];
|
||||||
|
for (int i = 0; i < logData.getColumns(); i++)
|
||||||
|
sample[i] = new Value(signals.get(i).getValue());
|
||||||
|
|
||||||
|
logData.add(new TestRow(sample));
|
||||||
|
clockHasChanged = false;
|
||||||
|
|
||||||
|
if (graphDialog == null || !graphDialog.isVisible()) {
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
String title = label;
|
||||||
|
if (title.isEmpty())
|
||||||
|
title = Lang.get("elem_ScopeTrigger_short");
|
||||||
|
graphDialog = new GraphDialog(model.getWindowPosManager().getMainFrame(), title, logData);
|
||||||
|
graphDialog.addWindowListener(new WindowAdapter() {
|
||||||
|
@Override
|
||||||
|
public void windowClosed(WindowEvent e) {
|
||||||
|
model.modify(() -> model.removeObserver(scopeModelStateObserver));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
graphDialog.setVisible(true);
|
||||||
|
model.getWindowPosManager().register("Scope_" + label, graphDialog);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -243,6 +243,16 @@
|
|||||||
Es können sowohl komplette Taktschritte als auch einzelne Gatter-Veränderungen angezeigt werden.
|
Es können sowohl komplette Taktschritte als auch einzelne Gatter-Veränderungen angezeigt werden.
|
||||||
Hat keine weitere Funktion für die Simulation.
|
Hat keine weitere Funktion für die Simulation.
|
||||||
</string>
|
</string>
|
||||||
|
<string name="elem_ScopeTrigger">Getriggerter Messwertgraph</string>
|
||||||
|
<string name="elem_ScopeTrigger_short">Scope</string>
|
||||||
|
<string name="elem_ScopeTrigger_tt">Zeigt einen Messwertgraph, wobei nur dann Messwerte gespeichert werden,
|
||||||
|
wenn sich das Eingangssignal verändert.
|
||||||
|
Das Speichern findet statt, wenn sich die Schaltung stabilisiert hat.
|
||||||
|
Der Trigger startet nicht die Messung wie bei einem echten Scope, sondern jedes Triggerereignis
|
||||||
|
speichert einen einzigen Messwert für jedes der angezeigten Signale.
|
||||||
|
</string>
|
||||||
|
<string name="elem_ScopeTrigger_pin_T">Ein Änderung an diesem Eingang veranlasst das Speichern von Messwerten.</string>
|
||||||
|
|
||||||
<string name="elem_RotEncoder">Drehencoder</string>
|
<string name="elem_RotEncoder">Drehencoder</string>
|
||||||
<string name="elem_RotEncoder_tt">Drehknopf mit Drehencoder zur Erfassung einer Drehbewegung.</string>
|
<string name="elem_RotEncoder_tt">Drehknopf mit Drehencoder zur Erfassung einer Drehbewegung.</string>
|
||||||
<string name="elem_RotEncoder_pin_A">Encodersignal A</string>
|
<string name="elem_RotEncoder_pin_A">Encodersignal A</string>
|
||||||
|
@ -247,6 +247,15 @@
|
|||||||
You can plot complete clock cycles or single gate changes.
|
You can plot complete clock cycles or single gate changes.
|
||||||
Does not affect the simulation.
|
Does not affect the simulation.
|
||||||
</string>
|
</string>
|
||||||
|
<string name="elem_ScopeTrigger">Triggered Data Graph</string>
|
||||||
|
<string name="elem_ScopeTrigger_short">Scope</string>
|
||||||
|
<string name="elem_ScopeTrigger_tt">Shows a graph of measured values, whereby measured values are only stored if
|
||||||
|
the input signal changes. Storing takes place when the circuit has stabilized.
|
||||||
|
The trigger does not start the measurement like in a real scope, but each trigger event stores a single
|
||||||
|
measurement value for each of the shown signals.
|
||||||
|
</string>
|
||||||
|
<string name="elem_ScopeTrigger_pin_T">A change at this input causes measured values to be stored.</string>
|
||||||
|
|
||||||
<string name="elem_RotEncoder">Rotary Encoder</string>
|
<string name="elem_RotEncoder">Rotary Encoder</string>
|
||||||
<string name="elem_RotEncoder_tt">Rotary knob with rotary encoder. Used to detect rotational movements.</string>
|
<string name="elem_RotEncoder_tt">Rotary knob with rotary encoder. Used to detect rotational movements.</string>
|
||||||
<string name="elem_RotEncoder_pin_A">encoder signal A</string>
|
<string name="elem_RotEncoder_pin_A">encoder signal A</string>
|
||||||
|
@ -682,6 +682,11 @@
|
|||||||
</elementAttributes>
|
</elementAttributes>
|
||||||
<pos x="800" y="480"/>
|
<pos x="800" y="480"/>
|
||||||
</visualElement>
|
</visualElement>
|
||||||
|
<visualElement>
|
||||||
|
<elementName>ScopeTrigger</elementName>
|
||||||
|
<elementAttributes/>
|
||||||
|
<pos x="820" y="940"/>
|
||||||
|
</visualElement>
|
||||||
</visualElements>
|
</visualElements>
|
||||||
<wires>
|
<wires>
|
||||||
<wire>
|
<wire>
|
||||||
@ -940,6 +945,10 @@
|
|||||||
<p1 x="580" y="940"/>
|
<p1 x="580" y="940"/>
|
||||||
<p2 x="800" y="940"/>
|
<p2 x="800" y="940"/>
|
||||||
</wire>
|
</wire>
|
||||||
|
<wire>
|
||||||
|
<p1 x="800" y="940"/>
|
||||||
|
<p2 x="820" y="940"/>
|
||||||
|
</wire>
|
||||||
<wire>
|
<wire>
|
||||||
<p1 x="280" y="940"/>
|
<p1 x="280" y="940"/>
|
||||||
<p2 x="300" y="940"/>
|
<p2 x="300" y="940"/>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user