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:
@@ -0,0 +1,112 @@
|
||||
// What the device sheet shows of a device, read straight from the document:
|
||||
// the fields of FILIUS' settings panel of its kind (JHostKonfiguration,
|
||||
// JSwitchKonfiguration, JModemKonfiguration). A router's and a home router's
|
||||
// sheet show their name only, until their own panels follow.
|
||||
|
||||
import * as Y from 'yjs';
|
||||
import { defaultSwitchConfiguration, DEFAULT_SWITCH_RETENTION_MILLISECONDS, forwardsIPv4ByDefault, stableGeneratedMacAddress, type HostLabelPolicy, type NodeKind } from '@mdg-filius/core';
|
||||
import { modemPort } from '@mdg-filius/fls';
|
||||
import { inOrder, ROOTS } from './projectDoc.js';
|
||||
|
||||
export interface HostSheet {
|
||||
readonly kind: 'pc' | 'notebook';
|
||||
readonly name: string;
|
||||
/** Host.useIPAsName and useMACAsName. */
|
||||
readonly labels: { readonly ipAddress: boolean; readonly macAddress: boolean };
|
||||
/** The card's MAC address (the model keeps them in upper case, as Java shows them). */
|
||||
readonly macAddress: string;
|
||||
readonly ipAddress: string;
|
||||
readonly subnetMask: string;
|
||||
readonly defaultGateway: string;
|
||||
readonly dnsServer: string;
|
||||
readonly dhcpClient: boolean;
|
||||
readonly ipForwarding: boolean;
|
||||
readonly wireless: boolean;
|
||||
/** The SSID of the network the card joins, '' for none. */
|
||||
readonly ssid: string;
|
||||
/** Java offers the wireless card only while the cable's port is free, or the card is wireless already. */
|
||||
readonly wirelessPossible: boolean;
|
||||
/** The switches' SSIDs, in the order of the devices, each once: the networks to choose from. */
|
||||
readonly networks: readonly string[];
|
||||
}
|
||||
|
||||
export interface SwitchSheet {
|
||||
readonly kind: 'networkSwitch';
|
||||
readonly name: string;
|
||||
readonly ssid: string;
|
||||
readonly retentionSeconds: number;
|
||||
readonly cloud: boolean;
|
||||
}
|
||||
|
||||
export interface ModemSheet {
|
||||
readonly kind: 'remoteLink';
|
||||
readonly name: string;
|
||||
readonly port: number;
|
||||
}
|
||||
|
||||
export interface NameSheet {
|
||||
readonly kind: Exclude<NodeKind, 'pc' | 'notebook' | 'networkSwitch' | 'remoteLink'>;
|
||||
readonly name: string;
|
||||
}
|
||||
|
||||
export type DeviceSheet = HostSheet | SwitchSheet | ModemSheet | NameSheet;
|
||||
|
||||
const settingsOf = (node: Y.Map<unknown>) => node.get('settings') as Y.Map<unknown>;
|
||||
const setting = (node: Y.Map<unknown>, key: string) => settingsOf(node).get(key) as Y.Map<unknown> | undefined;
|
||||
|
||||
/** The sheet of a device, or undefined when the document has no such device. */
|
||||
export function deviceSheet(doc: Y.Doc, id: string): DeviceSheet | undefined {
|
||||
const all = doc.getMap<Y.Map<unknown>>(ROOTS.nodes);
|
||||
const node = all.get(id);
|
||||
if (node === undefined) return undefined;
|
||||
const kind = node.get('kind') as NodeKind;
|
||||
const name = node.get('name') as string;
|
||||
switch (kind) {
|
||||
case 'pc':
|
||||
case 'notebook': {
|
||||
const device = setting(node, 'device');
|
||||
const wireless = setting(node, 'wireless');
|
||||
const port = (node.get('ports') as Y.Array<Y.Map<unknown>>).get(0);
|
||||
const portId = port?.get('id') as string | undefined;
|
||||
const plugged = [...doc.getMap<Y.Map<unknown>>(ROOTS.links).values()].some((link) => link.get('sourceNode') === id || link.get('targetNode') === id);
|
||||
const policy = node.get('labelPolicy') as HostLabelPolicy;
|
||||
const isWireless = wireless?.get('isEnabled') === true;
|
||||
const networks = new Set<string>();
|
||||
for (const [other, candidate] of inOrder(all)) {
|
||||
if (candidate.get('kind') !== 'networkSwitch') continue;
|
||||
networks.add((setting(candidate, 'switch')?.get('ssid') as string | undefined) ?? defaultSwitchConfiguration(other).ssid);
|
||||
}
|
||||
return {
|
||||
kind,
|
||||
name,
|
||||
labels: { ipAddress: policy === 'ipAddress' || policy === 'ipAndMAC', macAddress: policy === 'macAddress' || policy === 'ipAndMAC' },
|
||||
macAddress: (port?.get('mac') as string | undefined) ?? (portId === undefined ? '' : stableGeneratedMacAddress(portId)),
|
||||
ipAddress: (device?.get('ipAddress') as string | undefined) ?? '',
|
||||
subnetMask: (device?.get('subnetMask') as string | undefined) ?? '',
|
||||
defaultGateway: (device?.get('defaultGateway') as string | undefined) ?? '',
|
||||
dnsServer: (device?.get('dnsServer') as string | undefined) ?? '',
|
||||
dhcpClient: setting(node, 'dhcpClient')?.get('isEnabled') === true,
|
||||
ipForwarding: (settingsOf(node).get('ipForwarding') as boolean | undefined) ?? forwardsIPv4ByDefault(kind),
|
||||
wireless: isWireless,
|
||||
ssid: (wireless?.get('ssid') as string | undefined) ?? '',
|
||||
wirelessPossible: isWireless || !plugged,
|
||||
networks: [...networks],
|
||||
};
|
||||
}
|
||||
case 'networkSwitch': {
|
||||
const config = setting(node, 'switch');
|
||||
return {
|
||||
kind,
|
||||
name,
|
||||
ssid: (config?.get('ssid') as string | undefined) ?? defaultSwitchConfiguration(id).ssid,
|
||||
// Whole seconds, as Java's panel shows them (retentionTime / 1000 in long arithmetic).
|
||||
retentionSeconds: Math.trunc(((config?.get('retentionTimeMilliseconds') as number | undefined) ?? DEFAULT_SWITCH_RETENTION_MILLISECONDS) / 1000),
|
||||
cloud: config?.get('showsAsCloud') === true,
|
||||
};
|
||||
}
|
||||
case 'remoteLink':
|
||||
return { kind, name, port: modemPort(setting(node, 'remoteLink')?.get('pairIdentifier') as string | undefined) };
|
||||
default:
|
||||
return { kind, name };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user