feat: F4.6g1 the echo server and the generic client windows
Oracle EchoWindowRun uses Java's echo server and client windows as a person does; SaveAfterRunRun echo saves what Java keeps after the run. The room takes setActive with a port, connect/disconnect/send, sends both views and writes the server's port and the client's target back. The editor's windows show Java's buttons, fields and lines; a port that is no port is marked and not handed over (Java's button throws). Core fix: an empty message (one empty segment with the push, as Java's erstelleSegmente makes) counted as a repeat and never arrived. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -20,8 +20,9 @@
|
||||
//
|
||||
// A program's window may show more than its lines (plan §7.3): the browser's
|
||||
// page, the web server's button and table, the e-mail program's account and
|
||||
// folders, the e-mail server's domain and accounts go to the clients as a
|
||||
// view, whole, when they change.
|
||||
// folders, the e-mail server's domain and accounts, the echo server's button
|
||||
// and port, the generic client's connection go to the clients as a view,
|
||||
// whole, when they change.
|
||||
//
|
||||
// While the simulation runs, the document's programs are watched: a program
|
||||
// installed or removed on the desktop (Java's software installation) is
|
||||
@@ -70,6 +71,8 @@ import {
|
||||
type Topic,
|
||||
type EmailClientView,
|
||||
type EmailServerView,
|
||||
type EchoServerView,
|
||||
type SimpleClientView,
|
||||
type TraceFrame,
|
||||
type WebServerView,
|
||||
} from '@mdg-filius/protocol';
|
||||
@@ -237,7 +240,12 @@ export class SimulationRoom {
|
||||
settings.emailServer = emailServerAfterRun(configured as never, mailServerSettings);
|
||||
}
|
||||
const echo = this.engine.echoServer(id);
|
||||
if (echo !== undefined) active.echoServer = echo.isActive;
|
||||
if (echo !== undefined) {
|
||||
active.echoServer = echo.isActive;
|
||||
settings.echoServer = { port: echo.port };
|
||||
}
|
||||
const client = this.engine.simpleClient(id);
|
||||
if (client !== undefined) settings.simpleClient = { targetAddress: client.targetAddress, targetPort: client.targetPort };
|
||||
settings.emailClient = snapshot.emailClientsByNodeId.get(id);
|
||||
settings.firewall = snapshot.firewallConfigurationsByNodeId.get(id);
|
||||
if (snapshot.dhcpClientConfigurationsByNodeId.get(id)?.isEnabled === true) settings.device = snapshot.deviceConfigurations.get(id);
|
||||
@@ -442,6 +450,49 @@ export class SimulationRoom {
|
||||
return this.actInEmailClient(input);
|
||||
case 'emailServer':
|
||||
return this.actInEmailServer(input);
|
||||
case 'echoServer':
|
||||
return this.actInEchoServer(input);
|
||||
case 'simpleClient':
|
||||
return this.actInSimpleClient(input);
|
||||
default:
|
||||
return 'unknownAction';
|
||||
}
|
||||
}
|
||||
|
||||
/** The start button with the port typed (Java's window sets the port before and after it starts the server). */
|
||||
private actInEchoServer(input: Extract<ClientMessage, { kind: 'input' }>): ErrorCode | undefined {
|
||||
const server = this.engine.echoServer(input.node);
|
||||
if (server === undefined) return 'unknownApp';
|
||||
const { args } = input;
|
||||
if (input.action !== 'setActive') return 'unknownAction';
|
||||
if (typeof args.active !== 'boolean') return 'badArgs';
|
||||
if (!args.active) {
|
||||
server.setActive(false);
|
||||
return undefined;
|
||||
}
|
||||
if (!isPort(args.port)) return 'badArgs';
|
||||
server.setPort(args.port);
|
||||
server.setActive(true);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** "Verbinden" with the address and port typed, "Trennen", "Senden". */
|
||||
private actInSimpleClient(input: Extract<ClientMessage, { kind: 'input' }>): ErrorCode | undefined {
|
||||
const client = this.engine.simpleClient(input.node);
|
||||
if (client === undefined) return 'unknownApp';
|
||||
const { args } = input;
|
||||
switch (input.action) {
|
||||
case 'connect':
|
||||
if (typeof args.address !== 'string' || !isPort(args.port)) return 'badArgs';
|
||||
client.connect(args.address, args.port);
|
||||
return undefined;
|
||||
case 'disconnect':
|
||||
client.disconnect();
|
||||
return undefined;
|
||||
case 'send':
|
||||
if (typeof args.text !== 'string') return 'badArgs';
|
||||
client.send(args.text);
|
||||
return undefined;
|
||||
default:
|
||||
return 'unknownAction';
|
||||
}
|
||||
@@ -583,6 +634,18 @@ export class SimulationRoom {
|
||||
const view: EmailClientView = { account: client.account, inbox: client.inbox.map((entry) => entry.mail), sent: client.sent.map((entry) => entry.mail) };
|
||||
return view;
|
||||
}
|
||||
case 'echoServer': {
|
||||
const server = this.engine.echoServer(node);
|
||||
if (server === undefined) return null;
|
||||
const view: EchoServerView = { active: server.isActive, port: server.port };
|
||||
return view;
|
||||
}
|
||||
case 'simpleClient': {
|
||||
const client = this.engine.simpleClient(node);
|
||||
if (client === undefined) return null;
|
||||
const view: SimpleClientView = { connected: client.isConnected, address: client.targetAddress, port: client.targetPort };
|
||||
return view;
|
||||
}
|
||||
case 'emailServer': {
|
||||
const server = this.engine.emailServer(node);
|
||||
if (server === undefined) return null;
|
||||
@@ -732,6 +795,11 @@ export class SimulationRoom {
|
||||
}
|
||||
}
|
||||
|
||||
/** A port a socket can have. */
|
||||
function isPort(value: unknown): value is number {
|
||||
return Number.isInteger(value) && (value as number) >= 0 && (value as number) <= 65_535;
|
||||
}
|
||||
|
||||
/** A card's frames as the capture window shows them, both views. */
|
||||
export function traceFramesOf(frames: readonly CapturedFrame[]): TraceFrame[] {
|
||||
const byNumber = (rows: readonly CaptureRow[]) => {
|
||||
|
||||
Reference in New Issue
Block a user