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:
@@ -27,7 +27,10 @@
|
||||
// taken, and the sheet says why. Nor that Java's firewall table writes a
|
||||
// cell into the rule before it checks it, so that a rule holds "abc" or a
|
||||
// mask of 255.0.255.0 while the table shows the old value, and reads a port
|
||||
// that is no number as "all ports".
|
||||
// that is no number as "all ports". Nor that Java's connections dialog
|
||||
// removes a router's last card too: a router without one keeps no gateway
|
||||
// (setStandardGateway writes it into the cards; SheetRun's "connections"),
|
||||
// nor could a file hold it; here one card stays.
|
||||
|
||||
import * as Y from 'yjs';
|
||||
import {
|
||||
@@ -47,7 +50,7 @@ import {
|
||||
type NodeKind,
|
||||
} from '@mdg-filius/core';
|
||||
import { modemPair } from '@mdg-filius/fls';
|
||||
import { connect, type ConnectError } from './commands.js';
|
||||
import { connect, ROUTER_INTERFACES, type ConnectError } from './commands.js';
|
||||
import { checkField, type CheckedField, type FieldRule } from './fields.js';
|
||||
import { inOrder, ROOTS } from './projectDoc.js';
|
||||
import { fromY, toY, type PlainValue } from './values.js';
|
||||
@@ -295,6 +298,87 @@ export function setRip(doc: Y.Doc, id: Id, on: boolean, origin: unknown): void {
|
||||
}, origin);
|
||||
}
|
||||
|
||||
/** A router's ports, or none for another device. */
|
||||
function routerPorts(doc: Y.Doc, id: Id): Y.Array<Y.Map<unknown>> | undefined {
|
||||
const node = nodes(doc).get(id);
|
||||
return node === undefined || kindOf(node) !== 'router' ? undefined : portsOf(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* A card added to a router after its last (JConnectionsDialog's "+":
|
||||
* hinzuAnschluss, a new NetzwerkInterface with 192.168.0.10/24 and its own
|
||||
* MAC address, here from the port's id), up to eight; says whether it was.
|
||||
*/
|
||||
export function addRouterInterface(doc: Y.Doc, id: Id, portId: Id, origin: unknown): boolean {
|
||||
const ports = routerPorts(doc, id);
|
||||
if (ports === undefined || ports.length >= ROUTER_INTERFACES.maximum) return false;
|
||||
doc.transact(() => {
|
||||
const port = new Y.Map<unknown>();
|
||||
port.set('id', portId);
|
||||
port.set('label', `rt${ports.length + 1}`);
|
||||
port.set('occupied', false);
|
||||
port.set('config', toY({ ipAddress: '192.168.0.10', subnetMask: '255.255.255.0' }));
|
||||
ports.push([port]);
|
||||
}, origin);
|
||||
return true;
|
||||
}
|
||||
|
||||
type PluggedEnd = { readonly linkId: Id; readonly link: Y.Map<unknown>; readonly end: 'sourcePort' | 'targetPort' };
|
||||
|
||||
/** The cables at a device's port, with the end that plugs into it. */
|
||||
function cablesAt(doc: Y.Doc, id: Id, portId: Id): PluggedEnd[] {
|
||||
const ends: PluggedEnd[] = [];
|
||||
for (const [linkId, link] of links(doc).entries()) {
|
||||
if (link.get('sourceNode') === id && link.get('sourcePort') === portId) ends.push({ linkId, link, end: 'sourcePort' });
|
||||
else if (link.get('targetNode') === id && link.get('targetPort') === portId) ends.push({ linkId, link, end: 'targetPort' });
|
||||
}
|
||||
return ends;
|
||||
}
|
||||
|
||||
/** A router's last card removed with its cable (JConnectionsDialog's "-"), down to one; says whether it was. */
|
||||
export function removeRouterInterface(doc: Y.Doc, id: Id, origin: unknown): boolean {
|
||||
const ports = routerPorts(doc, id);
|
||||
if (ports === undefined || ports.length <= ROUTER_INTERFACES.fewest) return false;
|
||||
const last = ports.get(ports.length - 1).get('id') as Id;
|
||||
doc.transact(() => {
|
||||
for (const { linkId } of cablesAt(doc, id, last)) links(doc).delete(linkId);
|
||||
ports.delete(ports.length - 1);
|
||||
}, origin);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Two of a router's cards trade their cables (JConnectionsDialog.swapConnection):
|
||||
* both plugged, the cables swap; one plugged, its cable moves to the other
|
||||
* card; neither, nothing changes. The cables stay the same cables, joining the
|
||||
* same devices. Says whether a cable moved.
|
||||
*/
|
||||
export function swapRouterCables(doc: Y.Doc, id: Id, first: Id, second: Id, origin: unknown): boolean {
|
||||
const ports = routerPorts(doc, id);
|
||||
const has = (portId: Id) => ports?.toArray().some((port) => port.get('id') === portId) === true;
|
||||
if (first === second || !has(first) || !has(second)) return false;
|
||||
const atFirst = cablesAt(doc, id, first);
|
||||
const atSecond = cablesAt(doc, id, second);
|
||||
if (atFirst.length === 0 && atSecond.length === 0) return false;
|
||||
doc.transact(() => {
|
||||
for (const { link, end } of atFirst) link.set(end, second);
|
||||
for (const { link, end } of atSecond) link.set(end, first);
|
||||
}, origin);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* A click on a card's button in Java's connections dialog, with the card
|
||||
* marked before (null for none): the first click marks a card, a click on it
|
||||
* again unmarks it, a click on another card swaps the two cards' cables
|
||||
* (swapRouterCables) and leaves none marked.
|
||||
*/
|
||||
export function connectionsClick(marked: number | null, clicked: number): { readonly marked: number | null; readonly swap?: readonly [number, number] } {
|
||||
if (marked === null) return { marked: clicked };
|
||||
if (marked === clicked) return { marked: null };
|
||||
return { marked: null, swap: [marked, clicked] };
|
||||
}
|
||||
|
||||
/** A column of the forwarding table's manual rows. */
|
||||
export type RouteColumn = keyof ManualRoute;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user