feat(editor): F4.5a the device sheet for PCs, notebooks, switches and modems
Measured first: the new oracle SheetRun types 77 texts into FILIUS' own settings panels and records what was marked red, what the device took and what the field showed afterwards. Java's two judgements disagree (red text taken, black text refused, any int as a modem port, a refused text dropped without a word); here one rule decides both (packages/doc fields.ts), a refused text stays marked in the field, and only what Java's model accepts reaches the document. - doc: settings.ts (name, label policy, addresses, IP forwarding, DHCP stopping the own DHCP server, LAN/WLAN with the wireless connection, switch SSID dropping its wireless hosts, retention time, cloud, modem port as its pair), sheet.ts (what the sheet shows) - fls: modemPair/modemPort in one place - editor: DeviceSheet beside the surface or below it on a phone, opened with the selection; every change one undo step - not adopted from Java: the name overwritten by the MAC address, the access point search depending on unrelated devices Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1095,3 +1095,37 @@ Gemessen (03.10.2026):
|
||||
`msg26` (drahtlos nicht möglich), `msg12` (bereits verbunden).
|
||||
* Ein Wettlauf im Swing-Thread wirft beim Speichern gelegentlich eine
|
||||
Ausnahme; die Datei wird trotzdem vollständig geschrieben.
|
||||
|
||||
## Das Geräte-Blatt (F4.5a)
|
||||
|
||||
`SheetRun` tippt die Texte aus `packages/doc/test/sheet.cases.txt` in
|
||||
Filius' eigene Einstellungsfelder (`JHostKonfiguration`,
|
||||
`JSwitchKonfiguration`, `JModemKonfiguration`, per Reflection), ruft wie
|
||||
beim Verlassen eines Felds `aenderungenAnnehmen` und hält je Fall fest, ob
|
||||
das Feld beim Tippen rot war, was das Gerät danach hält und was das Feld
|
||||
dann zeigt (`packages/doc/test/sheet.java.json`). Jeder Fall beginnt mit
|
||||
einem neuen Gerät; ein Rechner hat Gateway 192.168.0.254 und DNS
|
||||
192.168.0.253, ein Switch die SSID „Start“, damit sichtbar wird, was ein
|
||||
Fall stehen lässt. Dazu drei Abläufe der Kästchen.
|
||||
|
||||
Gemessen (03.10.2026):
|
||||
|
||||
* Markieren (beim Tippen: das Muster auf den Text) und Übernehmen (beim
|
||||
Verlassen: `IP.ipCheck`, dann das Muster auf dessen Ergebnis) sind zwei
|
||||
Urteile, die sich widersprechen. Rot, aber übernommen: „10..0.0.1“,
|
||||
„+10.0.0.1“, „10.0.0.1.“, „١٠.0.0.1“, „-0.0.0.1“ (als 0.0.0.1),
|
||||
„255..255.0.0“, „192.168..1.1“, Haltezeit „-5“ (−5000 ms) und „٣“.
|
||||
Schwarz, aber verworfen: „.0.0.1“, Haltezeit „“ und
|
||||
„99999999999999999999“.
|
||||
* Gateway und DNS: Nur Leerzeichen gelten als leer (rot, aber übernommen);
|
||||
ein ungültiger Text lässt den alten Wert stehen.
|
||||
* Ein verworfener Text verschwindet ohne Meldung: Das Feld zeigt danach,
|
||||
was das Gerät hält.
|
||||
* Name: jeder Text, auch leer und mit Leerzeichen. SSID: druckbares ASCII,
|
||||
mindestens ein Zeichen, Leerzeichen eingeschlossen.
|
||||
* Modem: Der Port wird nie markiert und jede `int` übernommen (0, 65536,
|
||||
−1, „080“ als 80); nur Text, der keine Zahl ist, bleibt ohne Wirkung.
|
||||
* „MAC-Adresse als Name verwenden“, dann ein anderes Feld übernommen: Der
|
||||
Name wird zur MAC-Adresse und bleibt es nach dem Abschalten (bei der
|
||||
IP-Adresse nicht).
|
||||
* „DHCP zur Konfiguration verwenden“ schaltet den eigenen DHCP-Server ab.
|
||||
|
||||
@@ -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"
|
||||
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"
|
||||
# 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.
|
||||
@@ -328,6 +328,15 @@ rm -f "$output"
|
||||
HOME="$here/.build/home" xvfb-run -a java -cp "$classpath:$here/.build" NewDeviceRun "$output" 2>&1 | grep -v 'logback\|^$' || true
|
||||
test -s "$output"
|
||||
|
||||
# Text typed into FILIUS' settings panels of a host, a switch and a modem and
|
||||
# taken as they take it (SheetRun): what the editor's device sheet marks and
|
||||
# keeps (packages/doc/test/sheet.test.ts).
|
||||
output="$root/packages/doc/test/sheet.java.json"
|
||||
rm -f "$output"
|
||||
HOME="$here/.build/home" xvfb-run -a java -cp "$classpath:$here/.build" SheetRun \
|
||||
"$root/packages/doc/test/sheet.cases.txt" "$output" 2>&1 | grep -v 'logback\|^$' || 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,199 @@
|
||||
// Types into FILIUS' own settings panels (JHostKonfiguration,
|
||||
// JSwitchKonfiguration, JModemKonfiguration) and lets them take the text as
|
||||
// they do when a field loses the focus (aenderungenAnnehmen), then writes,
|
||||
// for each case, whether the panel marked the text red while typing, what the
|
||||
// device holds afterwards and what the field shows then (updateAttribute):
|
||||
// the reference for the editor's device sheet (packages/doc/test/sheet.test.ts).
|
||||
// Each case starts from a new device ("start": the field's value then). The cases file is ASCII, with
|
||||
// Java-style unicode escapes (backslash, u, four hex digits) for anything
|
||||
// else, one case a line:
|
||||
//
|
||||
// <host|switch|modem> <field> <text> the text may be empty
|
||||
//
|
||||
// Fields: host name, ip, mask, gateway, dns; switch name, ssid, retention;
|
||||
// modem name, port. After the cases, a few flows of the check boxes
|
||||
// ("flows"): a host's name while it shows its address or MAC address, and
|
||||
// DHCP switched on while its own DHCP server runs.
|
||||
//
|
||||
// Usage: java -cp filius.jar:lib/*:classes SheetRun <cases txt> <output json>
|
||||
|
||||
import filius.gui.GUIContainer;
|
||||
import filius.gui.netzwerksicht.JKonfiguration;
|
||||
import filius.hardware.knoten.Host;
|
||||
import filius.hardware.knoten.Modem;
|
||||
import filius.hardware.knoten.Rechner;
|
||||
import filius.hardware.knoten.Switch;
|
||||
import filius.rahmenprogramm.EingabenUeberpruefung;
|
||||
import filius.software.system.Betriebssystem;
|
||||
import filius.software.system.ModemFirmware;
|
||||
import filius.software.system.SwitchFirmware;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JTextField;
|
||||
|
||||
public class SheetRun {
|
||||
static Object get(Object panel, String name) throws Exception {
|
||||
Field field = panel.getClass().getDeclaredField(name);
|
||||
field.setAccessible(true);
|
||||
return field.get(panel);
|
||||
}
|
||||
|
||||
static void call(Object panel, String name) throws Exception {
|
||||
Method method = panel.getClass().getDeclaredMethod(name);
|
||||
method.setAccessible(true);
|
||||
method.invoke(panel);
|
||||
}
|
||||
|
||||
/** The panel's field and its check (the method its key listener calls), per device and field. */
|
||||
static final Map<String, String[]> FIELDS = new LinkedHashMap<>();
|
||||
static {
|
||||
FIELDS.put("host name", new String[] { "name", null });
|
||||
FIELDS.put("host ip", new String[] { "ipAdresse", "checkIpAddress" });
|
||||
FIELDS.put("host mask", new String[] { "netzmaske", "checkNetmask" });
|
||||
FIELDS.put("host gateway", new String[] { "gateway", "checkGatewayAddress" });
|
||||
FIELDS.put("host dns", new String[] { "dns", "checkDnsAddress" });
|
||||
FIELDS.put("switch name", new String[] { "name", null });
|
||||
FIELDS.put("switch ssid", new String[] { "ssid", "checkSSID" });
|
||||
FIELDS.put("switch retention", new String[] { "retentionTime", "checkRetentionTime" });
|
||||
FIELDS.put("modem name", new String[] { "name", null });
|
||||
FIELDS.put("modem port", new String[] { "tfPort", null });
|
||||
}
|
||||
|
||||
static String stored(Object device, String key) {
|
||||
if (device instanceof Host) {
|
||||
Host host = (Host) device;
|
||||
Betriebssystem os = (Betriebssystem) host.getSystemSoftware();
|
||||
switch (key) {
|
||||
case "host name": return host.getName();
|
||||
case "host ip": return os.primaryIPAdresse();
|
||||
case "host mask": return os.primarySubnetMask();
|
||||
case "host gateway": return os.getStandardGateway();
|
||||
case "host dns": return os.getDNSServer();
|
||||
}
|
||||
} else if (device instanceof Switch) {
|
||||
Switch sw = (Switch) device;
|
||||
SwitchFirmware firmware = (SwitchFirmware) sw.getSystemSoftware();
|
||||
switch (key) {
|
||||
case "switch name": return sw.getName();
|
||||
case "switch ssid": return firmware.getSSID();
|
||||
case "switch retention": return Long.toString(firmware.getRetentionTime());
|
||||
}
|
||||
} else if (device instanceof Modem) {
|
||||
Modem modem = (Modem) device;
|
||||
switch (key) {
|
||||
case "modem name": return modem.getName();
|
||||
case "modem port": return Integer.toString(((ModemFirmware) modem.getSystemSoftware()).getPort());
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException(key);
|
||||
}
|
||||
|
||||
/** A new device; a host with a gateway and a DNS server, a switch with a known SSID, so that what a case leaves alone shows. */
|
||||
static Object device(String kind) {
|
||||
switch (kind) {
|
||||
case "host": {
|
||||
Rechner host = new Rechner();
|
||||
Betriebssystem os = (Betriebssystem) host.getSystemSoftware();
|
||||
os.setStandardGateway("192.168.0.254");
|
||||
os.setDNSServer("192.168.0.253");
|
||||
return host;
|
||||
}
|
||||
case "switch": {
|
||||
Switch sw = new Switch();
|
||||
((SwitchFirmware) sw.getSystemSoftware()).setSSID("Start");
|
||||
return sw;
|
||||
}
|
||||
case "modem": return new Modem();
|
||||
}
|
||||
throw new IllegalArgumentException(kind);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// FILIUS' window keeps the JVM alive: end it on any failure too.
|
||||
try {
|
||||
run(args);
|
||||
} catch (Throwable error) {
|
||||
error.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
static void run(String[] args) throws Exception {
|
||||
GUIContainer.getGUIContainer();
|
||||
List<Object> cases = new ArrayList<>();
|
||||
for (String line : Files.readAllLines(Path.of(args[0]), StandardCharsets.US_ASCII)) {
|
||||
if (line.isEmpty() || line.startsWith("#")) continue;
|
||||
String[] words = line.split(" ", 3);
|
||||
String key = words[0] + " " + words[1];
|
||||
String text = words.length < 3 ? "" : IpCheckCases.unescape(words[2]);
|
||||
String[] names = FIELDS.get(key);
|
||||
if (names == null) throw new IllegalArgumentException(line);
|
||||
Object device = device(words[0]);
|
||||
String start = stored(device, key);
|
||||
JKonfiguration panel = JKonfiguration.getInstance((filius.hardware.Hardware) device);
|
||||
JTextField field = (JTextField) get(panel, names[0]);
|
||||
field.setText(text);
|
||||
boolean marked = false;
|
||||
if (names[1] != null) {
|
||||
call(panel, names[1]);
|
||||
marked = field.getForeground().equals(EingabenUeberpruefung.farbeFalsch);
|
||||
}
|
||||
call(panel, "aenderungenAnnehmen");
|
||||
panel.updateAttribute();
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("device", words[0]);
|
||||
result.put("field", words[1]);
|
||||
result.put("input", text);
|
||||
result.put("start", start);
|
||||
result.put("marked", marked);
|
||||
result.put("stored", stored(device, key));
|
||||
result.put("shown", field.getText());
|
||||
cases.add(result);
|
||||
}
|
||||
|
||||
Map<String, Object> flows = new LinkedHashMap<>();
|
||||
// The MAC address as name, then another field taken: what becomes of the name?
|
||||
for (String box : new String[] { "useMacAsName", "useIpAsName" }) {
|
||||
Rechner host = new Rechner();
|
||||
host.setName("PC-A");
|
||||
JKonfiguration panel = JKonfiguration.getInstance(host);
|
||||
((JCheckBox) get(panel, box)).setSelected(true);
|
||||
call(panel, "aendereAnzeigeName");
|
||||
((JTextField) get(panel, "gateway")).setText("192.168.0.1");
|
||||
call(panel, "aenderungenAnnehmen");
|
||||
((JCheckBox) get(panel, box)).setSelected(false);
|
||||
call(panel, "aendereAnzeigeName");
|
||||
flows.put(box + "ThenOff", host.getName());
|
||||
}
|
||||
// DHCP for the configuration while the host's own DHCP server is on.
|
||||
{
|
||||
Rechner host = new Rechner();
|
||||
Betriebssystem os = (Betriebssystem) host.getSystemSoftware();
|
||||
os.getDHCPServer().setAktiv(true);
|
||||
JKonfiguration panel = JKonfiguration.getInstance(host);
|
||||
((JCheckBox) get(panel, "dhcp")).setSelected(true);
|
||||
call(panel, "aenderungenAnnehmen");
|
||||
flows.put("dhcpClientStopsServer", !os.getDHCPServer().isAktiv());
|
||||
flows.put("dhcpClient", os.isDHCPKonfiguration());
|
||||
}
|
||||
|
||||
Map<String, Object> output = new LinkedHashMap<>();
|
||||
output.put("cases", cases);
|
||||
output.put("flows", flows);
|
||||
try (FileOutputStream out = new FileOutputStream(args[1])) {
|
||||
out.write((FlsOracle.Json.write(output, "") + "\n").getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
System.out.println("sheet cases: " + cases.size());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user