feat(editor): F4.5e a router's connections in the device sheet

SheetRun now clicks through Java's JConnectionsDialog with a router, a
computer and a switch cabled in the GUIContainer: a click marks a card,
the same click unmarks it, another card swaps the cables or moves the
one; "+" adds 192.168.0.10/24 up to eight cards, "-" removes the last
card with its cable down to none, and a router without a card keeps no
gateway.

The document adds, removes and swaps a router's cards (the same cables,
the same devices), Java's click logic is one function for the test and
the page, and the sheet names the address at a cable's far end. The
page "Verbindungen verwalten" shows Java's RJ45 buttons in Java's
colours with each card's far end below. Not adopted: the last card
stays. A card tab or a mark whose card is undone away falls back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-03 13:12:27 +00:00
co-authored by Claude Opus 5.5
parent 4e062bb347
commit 6e97e3565d
21 changed files with 2780 additions and 41 deletions
+28
View File
@@ -1211,3 +1211,31 @@ Gemessen (03.10.2026):
Ports“; „080“ wird 80.
* rauf/runter: Die erste Regel geht nicht höher, die letzte nicht tiefer
(`moveUp`/`moveDown` antworten `false`).
### Verbindungen verwalten (F4.5e)
`SheetRun` bedient auch `JConnectionsDialog` eines Vermittlungsrechners
(`connections`): drei Karten (10.0.0.1, 10.1.0.1, 10.2.0.1), Rechner A an
der ersten, Switch S an der zweiten, beide Kabel wie `GUIEvents`
(`createConnection`) sie in den `GUIContainer` legt; dann Klicks auf die
Knöpfe der Karten, „+“ und „-“, nach jedem Klick, was der Dialog zeigt
(Beschriftungen, Knopffarben, ob „+“/„-“ frei sind) und woran die Karten
hängen; bei einer und keiner Karte, ob das Router-Panel noch zeichnet und ob
der Router ein Gateway behält.
Gemessen (03.10.2026):
* Ein Klick markiert eine Karte gelb, ein zweiter auf dieselbe hebt das auf,
ein Klick auf eine andere tauscht die Kabel (beide belegt) oder steckt das
eine um (eine frei, auch eine markierte freie Karte); zwei freie: nichts.
Danach ist keine Karte markiert, ebenso nach „+“ und „-“.
* Beschriftungen: lokal „NIC n: Adresse“, Gegenstelle „Name (Adresse)“ bei
einem Rechner oder Router (die Adresse seiner Karte am Kabel), beim Switch
nur der Name.
* „+“: eine Karte mit 192.168.0.10/255.255.255.0 und neuer MAC, auch
mehrfach dieselbe Adresse; bei acht Karten gesperrt.
* „-“: die letzte Karte samt Kabel; der Port am anderen Ende wird frei.
Gesperrt erst bei null Karten. Das Panel zeichnet mit einer und mit keiner
Karte; ohne Karte behält der Router kein Gateway (`setStandardGateway`
schreibt es in die Karten, `getStandardGateway` liest `null`).
+138 -1
View File
@@ -30,18 +30,30 @@
// (JPortForwardingDialog, "forwarding"). Last, a router's firewall dialog
// (JFirewallDialog, "firewall"): a new rule, a cell of a rule edited in its
// table as the table takes it (GatewayFirewallConfigTable.closeEditor), the
// rules moved and removed with the dialog's buttons.
// rules moved and removed with the dialog's buttons. Then a router's
// connections dialog (JConnectionsDialog, "connections"): its cards and what
// their cables join, cables swapped with two clicks, cards added up to the
// last and removed down to none, and the router's panel and gateway with one
// card and with none.
//
// Usage: java -cp filius.jar:lib/*:classes SheetRun <cases txt> <output json>
import filius.gui.GUIContainer;
import filius.gui.JMainFrame;
import filius.gui.netzwerksicht.GUIDesignSidebar;
import filius.gui.netzwerksicht.GUIKabelItem;
import filius.gui.netzwerksicht.GUIKnotenItem;
import filius.gui.netzwerksicht.JConnectionsDialog;
import filius.gui.netzwerksicht.JDHCPKonfiguration;
import filius.gui.netzwerksicht.JSidebarButton;
import filius.gui.netzwerksicht.JFirewallDialog;
import filius.gui.netzwerksicht.JKonfiguration;
import filius.gui.netzwerksicht.JPortForwardingDialog;
import filius.gui.netzwerksicht.JWeiterleitungsTabelle;
import filius.hardware.Kabel;
import filius.hardware.NetzwerkInterface;
import filius.hardware.Port;
import filius.hardware.knoten.Knoten;
import filius.hardware.knoten.Gateway;
import filius.hardware.knoten.Host;
import filius.hardware.knoten.Modem;
@@ -73,7 +85,11 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import java.awt.Color;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
import java.util.LinkedList;
@@ -471,6 +487,126 @@ public class SheetRun {
return flows;
}
/** A device on the design surface, as GUIContainer keeps it (NewDeviceRun.add). */
static GUIKnotenItem item(Knoten device, String icon) {
JSidebarButton label = new JSidebarButton(new ImageIcon(SheetRun.class.getResource("/" + icon + ".png")), device.holeHardwareTyp());
label.initTextAndUpdateLocation(device.holeAnzeigeName());
GUIKnotenItem item = new GUIKnotenItem();
item.setKnoten(device);
item.setImageLabel(label);
GUIContainer.getGUIContainer().getKnotenItems().add(item);
return item;
}
/** A cable as GUIEvents.createConnection plugs it, from the first device's port to the second's. */
static void cable(GUIKnotenItem one, Port first, GUIKnotenItem two, Port second) {
GUIKabelItem item = new GUIKabelItem();
item.getKabelpanel().setZiel1(one);
item.getKabelpanel().setZiel2(two);
item.setDasKabel(new Kabel(first, second));
GUIContainer.getGUIContainer().getCableItems().add(item);
}
/** What the dialog shows and what the router's cards are joined to. */
static Map<String, Object> connectionsState(JConnectionsDialog dialog, Vermittlungsrechner router) throws Exception {
Map<String, Object> state = new LinkedHashMap<>();
List<Object> local = new ArrayList<>();
List<Object> remote = new ArrayList<>();
List<Object> colours = new ArrayList<>();
for (int i = 0; i < 8; i++) {
local.add(((JLabel) get(dialog, "lblLocal#" + i)).getText());
remote.add(((JLabel) get(dialog, "lblRemote#" + i)).getText());
JButton button = (JButton) get(dialog, "btnLocal#" + i);
Color colour = button.getBackground();
colours.add(!button.isEnabled() ? "off" : colour.equals(Color.YELLOW) ? "marked" : colour.equals(Color.GREEN) ? "plugged" : colour.equals(Color.RED) ? "free" : "other");
}
state.put("local", local);
state.put("remote", remote);
state.put("buttons", colours);
state.put("add", ((JButton) get(dialog, "btnAddInterface")).isEnabled());
state.put("remove", ((JButton) get(dialog, "btnRemoveInterface")).isEnabled());
// Each card: its address, mask, and the device its cable joins ("" for none).
List<Object> cards = new ArrayList<>();
for (NetzwerkInterface nic : router.getNetzwerkInterfaces()) {
String joined = "";
if (nic.getPort().getVerbindung() != null) {
for (Port port : nic.getPort().getVerbindung().getAnschluesse()) {
if (port != nic.getPort()) {
for (GUIKnotenItem each : GUIContainer.getGUIContainer().getKnotenItems()) {
Knoten node = each.getKnoten();
boolean owns = node instanceof Switch ? ((Switch) node).getAnschluesse().contains(port)
: node instanceof filius.hardware.knoten.InternetKnoten && ((filius.hardware.knoten.InternetKnoten) node).getNetzwerkInterfaces().stream().anyMatch(other -> other.getPort() == port);
if (owns) joined = node.holeAnzeigeName();
}
}
}
}
cards.add(Arrays.asList(nic.getIp(), nic.getSubnetzMaske(), joined));
}
state.put("cards", cards);
state.put("cables", GUIContainer.getGUIContainer().getCableItems().size());
return state;
}
static Map<String, Object> connectionsFlows() throws Exception {
GUIContainer.getGUIContainer().getKnotenItems().clear();
GUIContainer.getGUIContainer().getCableItems().clear();
Vermittlungsrechner router = new Vermittlungsrechner();
router.setzeAnzahlAnschluesse(3);
router.setName("R");
String[] addresses = { "10.0.0.1", "10.1.0.1", "10.2.0.1" };
for (int i = 0; i < 3; i++) router.getNetzwerkInterfaces().get(i).setIp(addresses[i]);
Rechner host = new Rechner();
host.setName("A");
host.getNetzwerkInterfaces().get(0).setIp("10.0.0.2");
Switch sw = new Switch();
sw.setName("S");
GUIKnotenItem routerItem = item(router, GUIDesignSidebar.VERMITTLUNGSRECHNER);
GUIKnotenItem hostItem = item(host, GUIDesignSidebar.RECHNER);
GUIKnotenItem switchItem = item(sw, GUIDesignSidebar.SWITCH);
// The host plugs into the router (the host first), the router into the switch.
cable(hostItem, host.getNetzwerkInterfaces().get(0).getPort(), routerItem, router.getNetzwerkInterfaces().get(0).getPort());
cable(routerItem, router.getNetzwerkInterfaces().get(1).getPort(), switchItem, sw.holeFreienPort());
JConnectionsDialog dialog = new JConnectionsDialog(JMainFrame.getJMainFrame(), router);
List<Object> steps = new ArrayList<>();
// A click on a card's button ("c<i>"), "+" and "-".
String[] clicks = { "start", "c0", "c0", "c0", "c2", "c2", "c1", "c1", "c1", "c0", "+", "c2", "c3", "-", "-", "+", "c0", "c2", "c0", "c1", "+", "+", "+", "+", "+", "+", "-", "-", "-", "-", "-", "-", "-", "-" };
for (String click : clicks) {
if (click.startsWith("c")) ((JButton) get(dialog, "btnLocal#" + click.substring(1))).doClick();
else if (click.equals("+")) ((JButton) get(dialog, "btnAddInterface")).doClick();
else if (click.equals("-")) ((JButton) get(dialog, "btnRemoveInterface")).doClick();
Map<String, Object> step = new LinkedHashMap<>();
step.put("click", click);
step.putAll(connectionsState(dialog, router));
// The router's panel with this many cards: does it still draw?
if (router.getNetzwerkInterfaces().size() <= 1) {
String error = null;
try {
JKonfiguration panel = JKonfiguration.getInstance(router);
panel.updateAttribute();
} catch (RuntimeException thrown) {
error = thrown.getClass().getSimpleName();
}
step.put("panelError", error);
// Java keeps the gateway in the cards: does a router without one keep it?
VermittlungsrechnerBetriebssystem os = (VermittlungsrechnerBetriebssystem) router.getSystemSoftware();
os.setStandardGateway("10.0.0.254");
step.put("gateway", os.getStandardGateway());
os.setStandardGateway("");
}
steps.add(step);
}
// New cards: their MAC addresses differ from each other and from the first cards'.
List<String> macs = new ArrayList<>();
for (NetzwerkInterface nic : router.getNetzwerkInterfaces()) macs.add(nic.getMac());
Map<String, Object> flows = new LinkedHashMap<>();
flows.put("steps", steps);
flows.put("hostPortFree", host.getNetzwerkInterfaces().get(0).getPort().getVerbindung() == null);
flows.put("switchPortsFree", sw.getAnschluesse().stream().allMatch(port -> port.getVerbindung() == null));
dialog.dispose();
return flows;
}
public static void main(String[] args) throws Exception {
// FILIUS' window keeps the JVM alive: end it on any failure too.
try {
@@ -596,6 +732,7 @@ public class SheetRun {
output.put("dhcp", dhcpFlows());
output.put("forwarding", forwardingFlows());
output.put("firewall", firewallFlows());
output.put("connections", connectionsFlows());
output.put("edits", edits);
try (FileOutputStream out = new FileOutputStream(args[1])) {
out.write((FlsOracle.Json.write(output, "") + "\n").getBytes(StandardCharsets.UTF_8));