Files
circuitjs1/websocket/circuitws.js
2022-09-10 23:00:22 -07:00

183 lines
4.8 KiB
JavaScript

export class CircuitWS {
constructor(iframe) {
this._iframe = iframe;
this._autoshutoff = false;
this._ws_uri = null;
this._ws = null;
this._circuitjs_loaded = false;
this._iframe.addEventListener("load", (event) => this._on_iframe_load(event));
this._on_iframe_load();
}
reload_circuitjs(src_uri) {
this._iframe.src = src_uri;
}
connect(ws_uri) {
this._ws_uri = ws_uri;
if (ws_uri != null) {
this._ws = new WebSocket(ws_uri);
this._ws.onmessage = (event) => this._ws_message(event);
this._ws.onclose = (event) => this._ws_close(event);
}
}
initialize_parameters(query_params) {
if (query_params.has("ws")) {
this.connect(query_params.get("ws"));
}
if (query_params.has("autoshutoff")) {
this._autoshutoff = query_params.get("autoshutoff") != 0;
}
}
_respond(msg) {
if ((this._ws != null) && (this._ws.readyState == WebSocket.OPEN)) {
this._ws.send(JSON.stringify(msg));
} else {
console.log("Discarded response:", msg);
}
}
_respond_error(msg, error_code, error_text) {
msg.status = "error";
msg.text = error_text;
this._respond(msg);
}
_respond_event(event_code, event_data) {
const msg = {
"type": "event",
"code": event_code,
};
if (event_data) {
msg["data"] = event_data;
}
this._respond(msg);
}
async _ws_message(event) {
const msg = JSON.parse(event.data);
if (!msg.hasOwnProperty("cmd")) {
this._respond_error("no_cmd_in_request", "No 'cmd' element found in JSON request.")
return;
}
/* The default response layout */
let response = {
"type": "response",
"status": "ok",
"cmd": msg.cmd,
};
if (msg.hasOwnProperty("msgid")) {
response.msgid = msg.msgid;
}
/* Process commands first which do not require a simulator; they do not
* send a response. */
if (msg.cmd == "shutdown") {
this.connect(null);
this._ws.close();
this._shutdown();
return this._respond(response);
} else if (msg.cmd == "reload") {
const url = new URL(this._iframe.src);
url.search = "?" + (new URLSearchParams(msg.args).toString());
this._iframe.src = url.toString();
return this._respond(response);
}
if (!this.sim) {
return this._respond_error(response, "no_sim_running", "No simulation running (iframe not loaded yet?).")
}
if (msg.cmd == "status") {
response.data = {
"running": this.sim.isRunning(),
"time": this.sim.getTime(),
"timestep": this.sim.getTimeStep(),
};
} else if (msg.cmd == "set_running") {
this.sim.setSimRunning(msg.state);
} else if (msg.cmd == "set_timestep") {
if (!msg.hasOwnProperty("timestep")) {
return this._respond_error(response, "no_timestep_in_request", "No 'timestep' element found in JSON request.");
}
this.sim.setTimeStep(msg.timestep);
} else if (msg.cmd == "get_node_voltage") {
response.data = { };
for (let node_name of msg.nodes) {
response.data[node_name] = this.sim.getNodeVoltage(node_name);
}
} else if (msg.cmd == "get_elements") {
response.data = [ ];
for (let element of this.sim.getElements()) {
response.data.push({
"type": element.getType(),
"post_count": element.getPostCount(),
"info": element.getInfo(),
});
}
} else if (msg.cmd == "set_ext_voltage") {
for (const [name, value] of Object.entries(msg.voltages)) {
this.sim.setExtVoltage(name, value);
}
} else if (msg.cmd == "circuit_export") {
response.data = this.sim.exportCircuit();
} else if (msg.cmd == "circuit_import") {
if (!msg.hasOwnProperty("circuit")) {
return this._respond_error(response, "no_circuit_in_request", "No 'circuit' element found in JSON request.")
}
const subcircuits_only = !!msg.subcircuits_only;
this.sim.importCircuit(msg.circuit, subcircuits_only);
} else if (msg.cmd == "get_svg") {
this.sim.getCircuitAsSVG();
response.data = "deferred";
} else {
return this._respond_error(response, "unknown_cmd", "Unknown command: " + msg.cmd)
}
this._respond(response);
}
_shutdown() {
this._iframe.src = "about:blank";
}
_ws_close(event) {
/* Attempt to reconnect after a while if we're not in autoshutoff mode.
* Otherwise, shut down the simulator. */
if (this._autoshutoff) {
this._shutdown();
return;
}
setTimeout(() => {
this.connect(this._ws_uri);
}, 1000);
}
_on_iframe_load(event) {
this._iframe.contentWindow.oncircuitjsloaded = (cjs) => this._on_circuitjs_loaded(cjs);
}
_on_circuitjs_loaded(cjs) {
this._respond_event("reload_complete");
cjs.onsvgrendered = (cjs, svg_data) => this._on_circuitjs_svg_rendered(cjs, svg_data);
this._circuitjs_loaded = true;
}
_on_circuitjs_svg_rendered(cjs, svg_data) {
this._respond_event("svg_rendered", svg_data);
}
get sim() {
if (this._iframe.contentWindow == null) {
return null;
}
if (!this._circuitjs_loaded) {
return null;
}
return this._iframe.contentWindow.CircuitJS1;
}
}