feat(sim): F4.6f1 what a run leaves goes back into the document

The oracle SaveAfterRunRun saves Java's project after a run. The room writes
what a run leaves that Java keeps - files, running servers, virtual hosts,
mail servers' domains and accounts, mail programs' accounts and folders,
firewalls, DHCP clients' addresses - into the document as origin 'sim', at
most once a second, after each action and at the stop; writeRunState writes
only what differs. The command line now goes on in a folder made before a
restart, as Java's does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-03 17:05:11 +00:00
co-authored by Claude Opus 5.5
parent 11f13bca84
commit 3d6655e736
15 changed files with 705 additions and 22 deletions
+25
View File
@@ -1368,3 +1368,28 @@ Gemessen (03.10.2026):
* Löschen löscht in Javas Fenster die Zeile der zuletzt **angeklickten**
Liste: War zuvor eine gesendete Mail gewählt und ist im Posteingang schon
eine markiert, geht die gesendete.
### Was ein Lauf im Projekt hinterlässt (F4.6f1)
`SaveAfterRunRun` öffnet ein Beispiel, wie FILIUS es öffnet, startet es,
spielt eine Geschichte über die Programme (wie ihre Fenster sie aufrufen),
stoppt wie der Entwurfsmodus (`stopSimulation`: `beenden` jedes Knotens) und
speichert (`SzenarioVerwaltung.speichern`; Java speichert nur im Entwurf):
`packages/sim/test/fixtures/after-run-email.fls` (email_komplett: Ordner
und Datei aus der Befehlszeile, Konten gesetzt, eine Mail gesendet und
abgerufen, ein neues Konto, Webserver aus und virtuelle Hosts an, ein
E-Mail-Server aus mit neuer Domain) und `after-run-dhcp.fls`
(einfaches_rechnernetz_komplett, 10 s DHCP).
Gemessen (03.10.2026):
* Java speichert die Dateien, wie die Programme sie hinterließen (auch
`/www.conf/vhosts`, die Kontendateien), ob jeder Server läuft, das
Kästchen der virtuellen Hosts (nach jedem Start an, wenn die Datei da
ist), Domain und Konten der E-Mail-Server, Konto, Posteingang und
Gesendete der E-Mail-Programme (eine abgerufene Mail mit „\n“ am Ende).
* Die Adressen, die DHCP-Clients bekamen, stehen danach in ihren Karten,
mit Gateway „0.0.0.0“, wo der Server keines vergibt.
* Die Konten der Programme in email_komplett liegen nur in der alten
`kontoListe`, die FILIUS 2.14 nicht liest; das Orakel setzt sie, wie man
sie in den Dialog tippt (`setzeKonto`, `speichern`).
+13 -1
View File
@@ -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"
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"
# 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.
@@ -389,6 +389,18 @@ HOME="$here/.build/home" xvfb-run -a java -Duser.language=de -Duser.country=DE -
"$root/packages/core/test/scenarios/mail-lan.scenario.json" "$output" 2>&1 | grep -v 'logback\|^$\|^\s*at \|AWT-EventQueue' || true
test -s "$output"
# What a run leaves in FILIUS' project: an example started, a story played,
# stopped as the design mode stops it and saved (SaveAfterRunRun): the room's
# write-back into the document (packages/sim/test/writeBack.test.ts).
for run in email:email_komplett dhcp:einfaches_rechnernetz_komplett; do
story="${run%%:*}"
output="$root/packages/sim/test/fixtures/after-run-$story.fls"
rm -f "$output"
HOME="$here/.build/home" xvfb-run -a java -Duser.language=de -Duser.country=DE -cp "$classpath:$here/.build" SaveAfterRunRun \
"$root/corpus/java-examples/${run##*:}.fls" "$story" "$output" 2>&1 | grep -v 'logback\|^$\|^\s*at ' || true
test -s "$output"
done
# 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).
+149
View File
@@ -0,0 +1,149 @@
// What a run leaves in FILIUS' project: an example opened and started as
// FILIUS does (ScenarioNetwork.fromFile), a story played through the
// programs as their windows call them, the simulation stopped as the design
// mode stops it (GUIMainMenu.stopSimulation: every node's beenden) and the
// project saved (SzenarioVerwaltung.speichern; Java saves only in the design
// mode). The reference for the room's write-back into the document
// (packages/sim/test/writeBack.test.ts).
//
// SaveAfterRunRun <file.fls> <story> <out.fls>
//
// email email_komplett, with FILIUS' command line installed on Rechner 1
// before the start: Rechner 1 and 2 set their accounts (as typed
// into the program's dialog), Rechner 1 types "mkdir ordner" and
// "touch ordner/notiz.txt", writes to rechner2@filius.de; Rechner 2
// fetches; Mail-Server (filius.de) gets the account "neu"; the
// Web-Server is switched off, its virtual hosts on with the row
// a.de → ordner; Mail-Server (senior.de) is switched off and its
// domain set to senior.org
// dhcp einfaches_rechnernetz_komplett: the notebooks get their
// addresses from the server (10 s)
import filius.gui.GUIContainer;
import filius.gui.netzwerksicht.GUIKnotenItem;
import filius.hardware.knoten.Knoten;
import filius.rahmenprogramm.SzenarioVerwaltung;
import filius.software.email.Email;
import filius.software.email.EmailAnwendung;
import filius.software.email.EmailKonto;
import filius.software.email.EmailServer;
import filius.software.email.EmailUtils;
import filius.software.lokal.Terminal;
import filius.software.system.Betriebssystem;
import filius.software.system.InternetKnotenBetriebssystem;
import filius.software.www.WebServer;
import java.util.Arrays;
import java.util.StringTokenizer;
public class SaveAfterRunRun {
static ScenarioNetwork network;
static InternetKnotenBetriebssystem system(String name) {
Knoten knoten = network.nodes.get(name);
if (knoten == null) throw new IllegalStateException("no node " + name);
return (InternetKnotenBetriebssystem) knoten.getSystemSoftware();
}
/** GUIApplicationTerminalWindow on Enter: the line handed to the program. */
static void type(Terminal terminal, String line) throws Exception {
StringTokenizer tokens = new StringTokenizer(line, " ");
String command = tokens.nextToken();
String[] parameters = new String[3 + tokens.countTokens()];
Arrays.fill(parameters, "");
for (int index = 0; tokens.hasMoreTokens(); index++) parameters[index] = tokens.nextToken();
terminal.terminalEingabeAuswerten(command, parameters);
Thread.sleep(500);
}
/**
* The account as one types it into the program's dialog and saves it
* (kontoSpeichern: setzeKonto, speichern): the file keeps it only in the
* older kontoListe, which FILIUS 2.14 no longer reads.
*/
static EmailKonto account(EmailAnwendung program, String user, String server, String domain, String lastName) {
EmailKonto account = new EmailKonto();
account.setVorname("Rechner");
account.setNachname(lastName);
account.setBenutzername(user);
account.setPasswort(user);
account.setPop3port("110");
account.setPop3server(server);
account.setSmtpport("25");
account.setSmtpserver(server);
account.setEmailAdresse(user + "@" + domain);
program.setzeKonto(account);
program.speichern();
return account;
}
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.fromFile(args[0]);
String story = args[1];
if (story.equals("email")) {
((Betriebssystem) system("Rechner 1")).installAndStartAppIfAvailable("filius.software.lokal.Terminal");
}
network.start();
Thread.sleep(2000);
switch (story) {
case "email": {
Terminal terminal = (Terminal) system("Rechner 1").holeSoftware("filius.software.lokal.Terminal");
type(terminal, "mkdir ordner");
type(terminal, "touch ordner/notiz.txt");
EmailAnwendung one = (EmailAnwendung) system("Rechner 1").holeSoftware("filius.software.email.EmailAnwendung");
EmailKonto account = account(one, "rechner1", "141.99.50.232", "filius.de", "1");
Email mail = new Email();
mail.setAbsender(account.getVorname() + (!account.getNachname().isEmpty() ? (" " + account.getNachname()) : "") + " <" + account.getEmailAdresse() + ">");
mail.setEmpfaenger(EmailUtils.stringToAddressEntryList("rechner2@filius.de"));
mail.setCc(EmailUtils.stringToAddressEntryList(""));
mail.setBcc(EmailUtils.stringToAddressEntryList(""));
mail.setBetreff("Nach dem Lauf");
mail.setText("Erste Zeile\nZweite Zeile");
one.versendeEmail(account.getSmtpserver(), mail, account.getEmailAdresse());
Thread.sleep(8000);
EmailAnwendung two = (EmailAnwendung) system("Rechner 2").holeSoftware("filius.software.email.EmailAnwendung");
EmailKonto other = account(two, "rechner2", "141.99.50.232", "filius.de", "2");
two.emailsAbholenEmails(other.getBenutzername(), other.getPasswort(), other.getPop3port(), other.getPop3server());
Thread.sleep(8000);
((EmailServer) system("Mail-Server (filius.de)").holeSoftware("filius.software.email.EmailServer")).benutzerHinzufuegen("neu", "pw", "Nachname", "Vorname");
WebServer web = (WebServer) system("Web-Server").holeSoftware("filius.software.www.WebServer");
web.setAktiv(false);
web.setUseVHost(true);
web.changeVHostTable(0, 0, "a.de");
web.changeVHostTable(0, 1, "ordner");
EmailServer senior = (EmailServer) system("Mail-Server (senior.de)").holeSoftware("filius.software.email.EmailServer");
senior.setAktiv(false);
senior.setMailDomain("senior.org");
Thread.sleep(1000);
break;
}
case "dhcp":
Thread.sleep(10000);
break;
default:
throw new IllegalArgumentException("unknown story " + story);
}
// GUIMainMenu.stopSimulation, then the design mode's save.
for (GUIKnotenItem item : GUIContainer.getGUIContainer().getKnotenItems()) {
try {
item.getKnoten().getSystemSoftware().beenden();
} catch (Exception error) {}
}
Thread.sleep(1000);
GUIContainer gui = GUIContainer.getGUIContainer();
if (!SzenarioVerwaltung.getInstance().speichern(args[2], gui.getKnotenItems(), gui.getCableItems(), gui.getDocuItems())) {
throw new IllegalStateException("FILIUS could not save " + args[2]);
}
System.out.println("saved after the run: " + story);
}
}