Skip to content

Commit 59736a7

Browse files
authored
refactor: move program list styling to ProgramRunnerInfo (#134)
Depends on #129 This PR refactors the programs list logic to a new `ProgramRunnerInfo` class and fixes some issues.
1 parent f25779f commit 59736a7

File tree

4 files changed

+134
-34
lines changed

4 files changed

+134
-34
lines changed

src/graphics/renderables/device_info.ts

Lines changed: 5 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,10 @@ import { Device } from "../../types/devices";
33
import { DeviceType } from "../../types/devices/device";
44
import { ViewGraph } from "../../types/graphs/viewgraph";
55
import { RemoveDeviceMove } from "../../types/undo-redo";
6-
import { refreshElement, urManager } from "../../types/viewportManager";
7-
import {
8-
createDropdown,
9-
createRightBarButton,
10-
createTable,
11-
createRoutingTable,
12-
} from "../right_bar";
6+
import { urManager } from "../../types/viewportManager";
7+
import { createRightBarButton, createRoutingTable } from "../right_bar";
138
import { ProgramInfo } from "./program_info";
9+
import { ProgramRunnerInfo } from "./program_runner_info";
1410
import { StyledInfo } from "./styled_info";
1511

1612
export { ProgramInfo } from "./program_info";
@@ -60,28 +56,8 @@ export class DeviceInfo extends StyledInfo {
6056

6157
// First argument is to avoid a circular dependency
6258
addProgramRunner(runner: ProgramRunner, programs: ProgramInfo[]) {
63-
const programOptions = programs.map(({ name }, i) => {
64-
return { value: i.toString(), text: name };
65-
});
66-
const inputsContainer = document.createElement("div");
67-
let selectedProgram = programs[0];
68-
inputsContainer.replaceChildren(...selectedProgram.toHTML());
69-
this.inputFields.push(
70-
// Dropdown for selecting program
71-
createDropdown("Program", programOptions, "program-selector", (v) => {
72-
selectedProgram = programs[parseInt(v)];
73-
inputsContainer.replaceChildren(...selectedProgram.toHTML());
74-
}),
75-
inputsContainer,
76-
// Button to send a packet
77-
createRightBarButton("Start program", () => {
78-
const { name } = selectedProgram;
79-
console.log("Started program: ", name);
80-
const inputs = selectedProgram.getInputValues();
81-
runner.addRunningProgram(name, inputs);
82-
refreshElement();
83-
}),
84-
);
59+
const programRunnerInfo = new ProgramRunnerInfo(runner, programs);
60+
this.inputFields.push(...programRunnerInfo.toHTML());
8561
}
8662

8763
addRunningProgramsList(
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { ProgramRunner, RunningProgram } from "../../programs";
2+
import {
3+
createDropdown,
4+
createRightBarButton,
5+
createTable,
6+
Renderable,
7+
} from "../right_bar";
8+
import { ProgramInfo } from "./program_info";
9+
10+
export class ProgramRunnerInfo implements Renderable {
11+
private runner: ProgramRunner;
12+
13+
private inputFields: Node[] = [];
14+
15+
private runningProgramsTable: HTMLTableElement;
16+
17+
constructor(runner: ProgramRunner, programInfos: ProgramInfo[]) {
18+
this.runner = runner;
19+
20+
this.addPrograms(programInfos);
21+
this.addRunningProgramsList();
22+
}
23+
24+
private addPrograms(programs: ProgramInfo[]) {
25+
let selectedProgram = programs[0];
26+
27+
const programOptions = programs.map(({ name }, i) => {
28+
return { value: i.toString(), text: name };
29+
});
30+
const programInputs = document.createElement("div");
31+
programInputs.replaceChildren(...selectedProgram.toHTML());
32+
// Dropdown for selecting program
33+
const selectProgramDropdown = createDropdown(
34+
"Program",
35+
programOptions,
36+
"program-selector",
37+
(v) => {
38+
selectedProgram = programs[parseInt(v)];
39+
programInputs.replaceChildren(...selectedProgram.toHTML());
40+
},
41+
);
42+
// Button to run program
43+
const startProgramButton = createRightBarButton("Start program", () => {
44+
const { name } = selectedProgram;
45+
console.log("Started program: ", name);
46+
const inputs = selectedProgram.getInputValues();
47+
this.runner.addRunningProgram(name, inputs);
48+
this.refreshTable();
49+
});
50+
this.inputFields.push(
51+
selectProgramDropdown,
52+
programInputs,
53+
startProgramButton,
54+
);
55+
}
56+
57+
private addRunningProgramsList() {
58+
this.runningProgramsTable = this.generateProgramsTable();
59+
this.inputFields.push(this.runningProgramsTable);
60+
}
61+
62+
private createProgramsTable(
63+
runner: ProgramRunner,
64+
runningPrograms: RunningProgram[],
65+
) {
66+
const onDelete = (row: number) => {
67+
const { pid } = runningPrograms[row];
68+
const removedProgram = runner.removeRunningProgram(pid);
69+
runningPrograms = runningPrograms.filter((p) => p.pid !== pid);
70+
if (runningPrograms.length === 0) {
71+
this.refreshTable();
72+
}
73+
return removedProgram;
74+
};
75+
const rows = runningPrograms.map((program) => [
76+
program.pid.toString(),
77+
program.name,
78+
JSON.stringify(program.inputs),
79+
]);
80+
const headers = ["PID", "Name", "Inputs"];
81+
// TODO: make table editable?
82+
const table = createTable(headers, rows, { onDelete });
83+
table.classList.add("right-bar-table");
84+
return table;
85+
}
86+
87+
private refreshTable() {
88+
const newTable = this.generateProgramsTable();
89+
this.runningProgramsTable.replaceWith(newTable);
90+
this.runningProgramsTable = newTable;
91+
}
92+
93+
private generateProgramsTable() {
94+
const runningPrograms = this.runner.getRunningPrograms();
95+
if (runningPrograms.length === 0) {
96+
return document.createElement("table");
97+
} else {
98+
return this.createProgramsTable(this.runner, runningPrograms);
99+
}
100+
}
101+
102+
toHTML() {
103+
return this.inputFields;
104+
}
105+
}

src/programs/index.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,26 @@ export interface RunningProgram {
1313

1414
// Currently used only for Host, due to a circular dependency
1515
export interface ProgramRunner {
16-
addRunningProgram(name: string, inputs: string[]): void;
17-
removeRunningProgram(pid: Pid): void;
16+
/**
17+
* Adds a new program to run.
18+
* @param name program name
19+
* @param inputs program inputs
20+
* @returns the running program data
21+
*/
22+
addRunningProgram(name: string, inputs: string[]): RunningProgram;
23+
24+
/**
25+
* Lists running programs.
26+
* @returns the list of running programs
27+
*/
28+
getRunningPrograms(): RunningProgram[];
29+
30+
/**
31+
* Stops a running program.
32+
* @param pid running program ID
33+
* @returns `true` if the program exists and was stopped, `false` otherwise
34+
*/
35+
removeRunningProgram(pid: Pid): boolean;
1836
}
1937

2038
export interface Program {

src/types/devices/host.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ export class Host extends Device {
4848
const info = new DeviceInfo(this);
4949
info.addField("IP Address", this.ip.octets.join("."));
5050
info.addProgramRunner(this, programList);
51-
info.addRunningProgramsList(this, runningProgramsList);
5251
RightBar.getInstance().renderInfo(info);
5352
}
5453

@@ -78,6 +77,7 @@ export class Host extends Device {
7877
device.runningPrograms.push(runningProgram);
7978
});
8079
this.runProgram(runningProgram);
80+
return runningProgram;
8181
}
8282

8383
removeRunningProgram(pid: Pid) {
@@ -93,13 +93,14 @@ export class Host extends Device {
9393
const program = this.runningPrograms.get(pid);
9494
if (!program) {
9595
console.error("Program not found");
96-
return;
96+
return false;
9797
}
9898
program.stop();
9999
this.runningPrograms.delete(pid);
100+
return true;
100101
}
101102

102-
private getRunningPrograms() {
103+
getRunningPrograms() {
103104
const thisDevice = this.viewgraph.getDataGraph().getDevice(this.id);
104105
if (!isHost(thisDevice)) {
105106
console.error("Node is not a Host");

0 commit comments

Comments
 (0)