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
+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));