feat: F4.7 the data exchange window, measured against Java's
One row per frame (its highest layer's), Java's detail tree, the table as Java's text file, "Tabellen löschen" for every card (clearCapture in the room), a tab per card with Java's title, Java's layer colours as tokens, the chosen frame and autoscroll per tab. New oracle ExchangeWindowRun. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1623,3 +1623,41 @@ Gemessen (03.10.2026):
|
||||
zeigt `HH:mm:ss.SSS : ` und den Frame in Javas Textform
|
||||
(`[src=…, dest=…, type=0x800 | [id=…; ttl=64; …]]`; ein IP-Paket ohne sein
|
||||
Segment).
|
||||
|
||||
### Datenaustausch-Fenster (F4.7)
|
||||
|
||||
`ExchangeWindowRun` benutzt Javas Datenaustausch-Fenster wie eine Person:
|
||||
`AggregatedExchangeDialog`, das `filius.ini` wählt (`old-exchange-dialog=0`),
|
||||
mit den Details als `MessageDetailsPanel` (ohne die experimentelle
|
||||
Schichtenansicht, Javas Voreinstellung). Auf `web-lan` öffnet die Sonde die
|
||||
Karte des Browsers vor jedem Verkehr, lädt `www.filius.de/text.txt`, wählt
|
||||
jede Zeile und liest die Details, exportiert die Tabelle (`writeToStream`),
|
||||
öffnet die Karte des Servers daneben und die erste wieder, wählt „Tabellen
|
||||
löschen“ aus dem Menü der Tabelle (per `mousePressed`), lädt noch einmal
|
||||
und startet die Simulation ohne den Server neu (als wäre er im Entwurf
|
||||
entfernt); dann wird das Fenster aktiviert (`clearUnavailableComponents`).
|
||||
Nach jedem Schritt: Reitertitel, gewählter Reiter, die Zeilen jeder Tabelle
|
||||
und zum Vergleich `Lauscher.getDaten` (`packages/core/test/exchangeWindow.java.json`).
|
||||
|
||||
Gemessen (04.10.2026):
|
||||
|
||||
* Die Tabelle zeigt je Frame eine Zeile, die seiner höchsten Schicht (mit
|
||||
Adresse:Port); 17 Zeilen statt Javas 51 Schichtzeilen.
|
||||
* Reitertitel „Anna - 192.168.0.20“, beim Rechner mit der Adresse als
|
||||
Beschriftung nur „192.168.0.30“; ein zweites Öffnen wählt den Reiter.
|
||||
* Die Details: „Nr.: 1 / Zeit: 08:32:35.815“, je Schicht ihr Name, darunter
|
||||
`Quelle:`, `Ziel:`, `Protokoll:` auf 15 Zeichen aufgefüllt (leere fehlen),
|
||||
„Bemerkungen / Details: …“, bei der Anwendung „(83 Bytes)“; eine
|
||||
Bemerkung mit Zeilenumbruch oder über 60 Zeichen als eigener Knoten.
|
||||
* Der Export: Rahmen aus `=` um den Kopf, Breiten 10/20/40, die Bemerkung
|
||||
auf Wörter zu je höchstens 40 Zeichen, Zeilen mit CR LF.
|
||||
* „Tabellen löschen“ leert die Tabellen aller Karten; danach beginnen die
|
||||
Nummern bei 1. Das Menü: „Tabellen löschen“, „Als Textdatei exportieren“,
|
||||
„Automatisches Scrollen“.
|
||||
* Nach dem Neustart bleiben der Reiter des Browsers und seine 13 Zeilen;
|
||||
der Reiter des nicht gestarteten Servers verschwindet.
|
||||
|
||||
Fallen der Sonde: Die Einträge des Tabellenmenüs hören nur auf
|
||||
`mousePressed` (sie haben keinen `ActionListener`). Beim Stoppen versteckt Javas Oberfläche das Fenster
|
||||
(`closeDialogs`); ein Aktivieren im Entwurf, wie es die Sonde zuerst tat,
|
||||
entfernt alle Reiter, kommt aber in Javas Oberfläche nicht vor.
|
||||
|
||||
@@ -21,7 +21,7 @@ if [ ! -f "$upstream/target/filius.jar" ]; then
|
||||
fi
|
||||
classpath="$upstream/target/filius.jar:$upstream/target/lib/*"
|
||||
mkdir -p "$here/.build"
|
||||
javac -encoding UTF-8 -d "$here/.build" -cp "$classpath" "$here/src/FlsOracle.java" "$here/src/GetterDefaults.java" "$here/src/IpCheckCases.java" "$here/src/DnsHostsCases.java" "$here/src/EmailFileCases.java" "$here/src/BeansDump.java" "$here/src/Base64Cases.java" "$here/src/ScenarioNetwork.java" "$here/src/DhcpRun.java" "$here/src/TcpRun.java" "$here/src/DnsRun.java" "$here/src/ArpRun.java" "$here/src/RipRun.java" "$here/src/NatRun.java" "$here/src/WlanRun.java" "$here/src/ExampleRun.java" "$here/src/EchoRun.java" "$here/src/WebRun.java" "$here/src/RouterWebRun.java" "$here/src/KonfigCases.java" "$here/src/EmailRun.java" "$here/src/MailCases.java" "$here/src/EmailClientRun.java" "$here/src/TerminalCases.java" "$here/src/TerminalRun.java" "$here/src/FileCases.java" "$here/src/GnutellaRun.java" "$here/src/NewDeviceRun.java" "$here/src/SheetRun.java" "$here/src/TerminalWindowRun.java" "$here/src/InstallRun.java" "$here/src/BrowserWindowRun.java" "$here/src/MailWindowRun.java" "$here/src/SaveAfterRunRun.java" "$here/src/TextEditorWindowRun.java" "$here/src/FileExplorerWindowRun.java" "$here/src/EchoWindowRun.java" "$here/src/DnsWindowRun.java" "$here/src/FirewallWindowRun.java" "$here/src/GnutellaWindowRun.java" "$here/src/ViewersRun.java"
|
||||
javac -encoding UTF-8 -d "$here/.build" -cp "$classpath" "$here/src/FlsOracle.java" "$here/src/GetterDefaults.java" "$here/src/IpCheckCases.java" "$here/src/DnsHostsCases.java" "$here/src/EmailFileCases.java" "$here/src/BeansDump.java" "$here/src/Base64Cases.java" "$here/src/ScenarioNetwork.java" "$here/src/DhcpRun.java" "$here/src/TcpRun.java" "$here/src/DnsRun.java" "$here/src/ArpRun.java" "$here/src/RipRun.java" "$here/src/NatRun.java" "$here/src/WlanRun.java" "$here/src/ExampleRun.java" "$here/src/EchoRun.java" "$here/src/WebRun.java" "$here/src/RouterWebRun.java" "$here/src/KonfigCases.java" "$here/src/EmailRun.java" "$here/src/MailCases.java" "$here/src/EmailClientRun.java" "$here/src/TerminalCases.java" "$here/src/TerminalRun.java" "$here/src/FileCases.java" "$here/src/GnutellaRun.java" "$here/src/NewDeviceRun.java" "$here/src/SheetRun.java" "$here/src/TerminalWindowRun.java" "$here/src/InstallRun.java" "$here/src/BrowserWindowRun.java" "$here/src/MailWindowRun.java" "$here/src/SaveAfterRunRun.java" "$here/src/TextEditorWindowRun.java" "$here/src/FileExplorerWindowRun.java" "$here/src/EchoWindowRun.java" "$here/src/DnsWindowRun.java" "$here/src/FirewallWindowRun.java" "$here/src/GnutellaWindowRun.java" "$here/src/ViewersRun.java" "$here/src/ExchangeWindowRun.java"
|
||||
# FILIUS writes its settings below $HOME; keep them out of the real one.
|
||||
mkdir -p "$here/.build/home"
|
||||
# Removed first: a crashed run must not leave the previous result standing.
|
||||
@@ -477,6 +477,17 @@ for run in switch:switch-lan nat:nat-home; do
|
||||
test -s "$output"
|
||||
done
|
||||
|
||||
# FILIUS' data exchange window used as a person does — a card opened, a page
|
||||
# fetched by name, each row chosen and its details read, the table exported,
|
||||
# a second card beside it, "Tabellen löschen", the simulation started again
|
||||
# without one computer (ExchangeWindowRun): the capture window
|
||||
# (packages/core/test/exchangeWindow.test.ts).
|
||||
output="$root/packages/core/test/exchangeWindow.java.json"
|
||||
rm -f "$output"
|
||||
HOME="$here/.build/home" xvfb-run -a java -Duser.language=de -Duser.country=DE -cp "$classpath:$here/.build" ExchangeWindowRun \
|
||||
"$root/packages/core/test/scenarios/web-lan.scenario.json" "$output" 2>&1 | grep -v 'logback\|^$\|^\s*at ' || true
|
||||
test -s "$output"
|
||||
|
||||
# FILIUS' own examples as Java FILIUS runs them, opened and started as FILIUS
|
||||
# does (ExampleRun): the acceptance of F2 (packages/fls/test/examplesJava.test.ts)
|
||||
# and the classics of F3 (web, email, gnutella; packages/fls/test/classicsJava.test.ts).
|
||||
|
||||
@@ -0,0 +1,293 @@
|
||||
// Uses FILIUS' data exchange window (AggregatedExchangeDialog, the one
|
||||
// filius.ini chooses, with its details as MessageDetailsPanel, the default
|
||||
// without the layer visualization) on web-lan as a person does: the
|
||||
// browser's card opened before any traffic, a page fetched by name (DNS, then
|
||||
// HTTP), each row chosen and its details read, the table exported as text,
|
||||
// the server's card opened beside it and the first chosen again, "Tabellen
|
||||
// löschen" from the table's menu, a second fetch, the simulation stopped and
|
||||
// started again without the server (as if removed in the design) and the
|
||||
// window activated. After each step it writes what the window shows: the
|
||||
// tabs' titles, the chosen tab, each table's rows (all seven cells) and, for
|
||||
// comparison, the card's rows as Lauscher.getDaten gives them. The reference
|
||||
// for the editor's capture window (packages/core/test/exchangeWindow.test.ts).
|
||||
//
|
||||
// Usage: java -cp filius.jar:lib/*:classes ExchangeWindowRun <scenario.json> <output json>
|
||||
|
||||
import filius.gui.nachrichtensicht.AggregatedExchangeDialog;
|
||||
import filius.gui.nachrichtensicht.AggregatedMessageTable;
|
||||
import filius.hardware.knoten.Host;
|
||||
import filius.hardware.knoten.InternetKnoten;
|
||||
import filius.rahmenprogramm.nachrichten.Lauscher;
|
||||
import filius.software.system.Dateisystem;
|
||||
import filius.software.system.Datei;
|
||||
import filius.software.system.InternetKnotenBetriebssystem;
|
||||
import filius.software.www.WebBrowser;
|
||||
import filius.software.www.WebServer;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Hashtable;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JTabbedPane;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.table.TableModel;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
|
||||
public class ExchangeWindowRun {
|
||||
static final String BROWSER = "00000000-0000-0000-0000-000000000201";
|
||||
static final String SERVER = "00000000-0000-0000-0000-000000000203";
|
||||
|
||||
static final List<Object> steps = new ArrayList<>();
|
||||
static ScenarioNetwork network;
|
||||
static AggregatedExchangeDialog dialog;
|
||||
|
||||
static Object field(Class<?> type, Object object, String name) throws Exception {
|
||||
Field field = type.getDeclaredField(name);
|
||||
field.setAccessible(true);
|
||||
return field.get(object);
|
||||
}
|
||||
|
||||
static JTabbedPane panel() throws Exception {
|
||||
return (JTabbedPane) field(AggregatedExchangeDialog.class, dialog, "exchangePanel");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static AggregatedMessageTable table(String mac) throws Exception {
|
||||
return ((Hashtable<String, AggregatedMessageTable>) field(panel().getClass(), panel(), "tabellen")).get(mac);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static JPanel tab(String mac) throws Exception {
|
||||
return ((Hashtable<String, JPanel>) field(panel().getClass(), panel(), "openedTabs")).get(mac);
|
||||
}
|
||||
|
||||
static String mac(String node) {
|
||||
return ((InternetKnoten) network.nodes.get(node)).getNetzwerkInterfaces().get(0).getMac();
|
||||
}
|
||||
|
||||
static InternetKnotenBetriebssystem os(String node) {
|
||||
return network.system(node);
|
||||
}
|
||||
|
||||
static void onEdt(Runnable action) throws Exception {
|
||||
SwingUtilities.invokeAndWait(action);
|
||||
Thread.sleep(300);
|
||||
}
|
||||
|
||||
static List<List<String>> rows(TableModel model) {
|
||||
List<List<String>> rows = new ArrayList<>();
|
||||
for (int row = 0; row < model.getRowCount(); row++) {
|
||||
List<String> cells = new ArrayList<>();
|
||||
for (int column = 0; column < model.getColumnCount(); column++) {
|
||||
Object value = model.getValueAt(row, column);
|
||||
cells.add(value == null ? null : String.valueOf(value));
|
||||
}
|
||||
rows.add(cells);
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
static List<List<String>> lauscherRows(String mac, boolean withAddresses) {
|
||||
List<List<String>> rows = new ArrayList<>();
|
||||
for (Object[] row : Lauscher.getLauscher().getDaten(mac, withAddresses, 0)) {
|
||||
List<String> cells = new ArrayList<>();
|
||||
for (Object cell : row) cells.add(cell == null ? null : String.valueOf(cell));
|
||||
rows.add(cells);
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
/** The details below the table: each node of the tree, in order, as [depth, text]. */
|
||||
static List<Object> details(String mac) throws Exception {
|
||||
List<Object> nodes = new ArrayList<>();
|
||||
JTree tree = find(tab(mac), JTree.class);
|
||||
if (tree == null) return nodes;
|
||||
DefaultMutableTreeNode root = (DefaultMutableTreeNode) tree.getModel().getRoot();
|
||||
for (Enumeration<?> each = root.preorderEnumeration(); each.hasMoreElements();) {
|
||||
DefaultMutableTreeNode node = (DefaultMutableTreeNode) each.nextElement();
|
||||
List<Object> entry = new ArrayList<>();
|
||||
entry.add(node.getLevel());
|
||||
entry.add(String.valueOf(node.getUserObject()));
|
||||
nodes.add(entry);
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
static <T> T find(Component component, Class<T> type) {
|
||||
if (type.isInstance(component)) return type.cast(component);
|
||||
if (component instanceof Container) {
|
||||
for (Component child : ((Container) component).getComponents()) {
|
||||
T found = find(child, type);
|
||||
if (found != null) return found;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static Map<String, Object> window() throws Exception {
|
||||
Map<String, Object> shown = new LinkedHashMap<>();
|
||||
JTabbedPane tabs = panel();
|
||||
List<String> titles = new ArrayList<>();
|
||||
Method title = AggregatedExchangeComponentTitle.method();
|
||||
for (int index = 0; index < tabs.getTabCount(); index++) {
|
||||
Object component = tabs.getTabComponentAt(index);
|
||||
titles.add(component == null ? null : (String) title.invoke(component));
|
||||
}
|
||||
shown.put("titles", titles);
|
||||
shown.put("selected", tabs.getSelectedIndex());
|
||||
shown.put("visible", dialog.isVisible());
|
||||
Map<String, Object> tables = new LinkedHashMap<>();
|
||||
for (String node : new String[] { BROWSER, SERVER }) {
|
||||
AggregatedMessageTable table = table(mac(node));
|
||||
if (table == null) continue;
|
||||
Map<String, Object> each = new LinkedHashMap<>();
|
||||
each.put("rows", rows(table.getModel()));
|
||||
each.put("layerRows", lauscherRows(mac(node), true));
|
||||
tables.put(node, each);
|
||||
}
|
||||
shown.put("tables", tables);
|
||||
return shown;
|
||||
}
|
||||
|
||||
/** TabTitle.getTitle, a private class's method. */
|
||||
static final class AggregatedExchangeComponentTitle {
|
||||
static Method method() throws Exception {
|
||||
for (Class<?> inner : Class.forName("filius.gui.nachrichtensicht.AggregatedExchangePanel").getDeclaredClasses()) {
|
||||
if (inner.getSimpleName().equals("TabTitle")) {
|
||||
Method method = inner.getDeclaredMethod("getTitle");
|
||||
method.setAccessible(true);
|
||||
return method;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("no TabTitle");
|
||||
}
|
||||
}
|
||||
|
||||
static void step(String name, Map<String, Object> shown) {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("step", name);
|
||||
result.put("at", System.currentTimeMillis() - network.start);
|
||||
result.putAll(shown);
|
||||
steps.add(result);
|
||||
}
|
||||
|
||||
static void fetch(WebBrowser browser, String url) throws Exception {
|
||||
browser.holeWebseite(new URL(url));
|
||||
Thread.sleep(9000);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
try {
|
||||
run(args);
|
||||
} catch (Throwable error) {
|
||||
error.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
static void run(String[] args) throws Exception {
|
||||
network = ScenarioNetwork.read(args[0]);
|
||||
os(SERVER).installApp("filius.software.www.WebServer", false);
|
||||
os(BROWSER).installApp("filius.software.www.WebBrowser", false);
|
||||
WebServer server = (WebServer) os(SERVER).holeSoftware("filius.software.www.WebServer");
|
||||
WebBrowser browser = (WebBrowser) os(BROWSER).holeSoftware("filius.software.www.WebBrowser");
|
||||
Dateisystem files = os(SERVER).getDateisystem();
|
||||
files.speicherDatei("/webserver", new Datei("text.txt", "txt", "Nur Text\nzwei Zeilen"));
|
||||
server.setAktiv(true);
|
||||
network.nodes.get(BROWSER).setName("Anna");
|
||||
((Host) network.nodes.get(SERVER)).setUseIPAsName(true);
|
||||
network.start();
|
||||
Thread.sleep(500);
|
||||
|
||||
JFrame frame = new JFrame();
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
frame.setSize(1200, 900);
|
||||
frame.setVisible(true);
|
||||
dialog = AggregatedExchangeDialog.getInstance(frame);
|
||||
});
|
||||
onEdt(() -> {
|
||||
dialog.addTable(os(BROWSER), mac(BROWSER));
|
||||
dialog.setVisible(true);
|
||||
});
|
||||
step("open", window());
|
||||
|
||||
fetch(browser, "http://www.filius.de/text.txt");
|
||||
Map<String, Object> shown = window();
|
||||
// Each row chosen, the details below it.
|
||||
AggregatedMessageTable table = table(mac(BROWSER));
|
||||
List<Object> details = new ArrayList<>();
|
||||
for (int row = 0; row < table.getModel().getRowCount(); row++) {
|
||||
final int chosen = row;
|
||||
onEdt(() -> table.setRowSelectionInterval(chosen, chosen));
|
||||
details.add(details(mac(BROWSER)));
|
||||
}
|
||||
shown.put("details", details);
|
||||
shown.put("layered", lauscherRows(mac(BROWSER), false));
|
||||
ByteArrayOutputStream export = new ByteArrayOutputStream();
|
||||
table.writeToStream(export);
|
||||
shown.put("export", export.toString(StandardCharsets.UTF_8.name()));
|
||||
step("after the fetch", shown);
|
||||
|
||||
onEdt(() -> dialog.addTable(os(SERVER), mac(SERVER)));
|
||||
step("the server's card beside it", window());
|
||||
onEdt(() -> dialog.addTable(os(BROWSER), mac(BROWSER)));
|
||||
step("the browser's card again", window());
|
||||
|
||||
// "Tabellen löschen", the first entry of the table's menu (without the layer visualization).
|
||||
JPopupMenu menu = (JPopupMenu) field(AggregatedMessageTable.class, table, "menu");
|
||||
List<String> entries = new ArrayList<>();
|
||||
for (Component entry : menu.getComponents()) entries.add(((JMenuItem) entry).getText());
|
||||
JMenuItem clear = (JMenuItem) menu.getComponent(0);
|
||||
onEdt(() -> clear.dispatchEvent(new MouseEvent(clear, MouseEvent.MOUSE_PRESSED, System.currentTimeMillis(), 0, 1, 1, 1, false, MouseEvent.BUTTON1)));
|
||||
Map<String, Object> cleared = window();
|
||||
cleared.put("menu", entries);
|
||||
step("cleared", cleared);
|
||||
|
||||
fetch(browser, "http://www.filius.de/text.txt");
|
||||
step("fetched again", window());
|
||||
|
||||
// Stopped and started again (GUIContainer.closeDialogs hides the window, reopenDialogs shows it): the server's
|
||||
// computer as if removed in the design, its system not started again; then the window activated.
|
||||
network.stop();
|
||||
Thread.sleep(500);
|
||||
for (Map.Entry<String, filius.hardware.knoten.Knoten> node : network.nodes.entrySet()) {
|
||||
if (!node.getKey().equals(SERVER)) node.getValue().getSystemSoftware().starten();
|
||||
}
|
||||
Thread.sleep(500);
|
||||
Method clearUnavailable = panel().getClass().getDeclaredMethod("clearUnavailableComponents");
|
||||
clearUnavailable.setAccessible(true);
|
||||
onEdt(() -> {
|
||||
try {
|
||||
clearUnavailable.invoke(panel());
|
||||
} catch (Exception error) {
|
||||
throw new RuntimeException(error);
|
||||
}
|
||||
});
|
||||
step("started again without the server", window());
|
||||
network.stop();
|
||||
|
||||
Map<String, Object> output = new LinkedHashMap<>();
|
||||
output.put("steps", steps);
|
||||
try (FileOutputStream out = new FileOutputStream(args[1])) {
|
||||
out.write((FlsOracle.Json.write(output, "") + "\n").getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
System.out.println("exchange window steps: " + steps.size());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user