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:
2026-10-03 11:22:28 +00:00
co-authored by Claude Opus 5.5
parent 806df6baca
commit 9b7ec2fc72
25 changed files with 2667 additions and 73 deletions
+199
View File
@@ -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());
}
}