mirror of
https://github.com/ad1217/PrinterStatus
synced 2024-11-24 08:23:48 -05:00
server: Actively check for loss of connection to octoprint websocket
Periodically `ping` the server, and check for returning `pong`s
This commit is contained in:
parent
51d3cbf41e
commit
009612898a
@ -13,6 +13,10 @@ import * as octoprint from '../types/octoprint';
|
|||||||
|
|
||||||
const PORT = process.env.PORT || 1234;
|
const PORT = process.env.PORT || 1234;
|
||||||
|
|
||||||
|
const PING_TIME = 10000;
|
||||||
|
|
||||||
|
type Timeout = ReturnType<typeof setTimeout>;
|
||||||
|
|
||||||
type configuration = {
|
type configuration = {
|
||||||
printers: {
|
printers: {
|
||||||
[key: string]: { address: string; apikey: string };
|
[key: string]: { address: string; apikey: string };
|
||||||
@ -69,9 +73,6 @@ class PrinterStatus {
|
|||||||
|
|
||||||
webcamURL?: URL;
|
webcamURL?: URL;
|
||||||
name?: string;
|
name?: string;
|
||||||
session_key?: string;
|
|
||||||
|
|
||||||
websocket?: WebSocket;
|
|
||||||
lastStatus?: messages.ExtendedMessage;
|
lastStatus?: messages.ExtendedMessage;
|
||||||
|
|
||||||
constructor(slug: string, address: string, apikey: string) {
|
constructor(slug: string, address: string, apikey: string) {
|
||||||
@ -79,42 +80,43 @@ class PrinterStatus {
|
|||||||
this.address = address;
|
this.address = address;
|
||||||
this.apikey = apikey;
|
this.apikey = apikey;
|
||||||
|
|
||||||
// async init
|
this.try_connect_websocket();
|
||||||
|
}
|
||||||
|
|
||||||
|
try_connect_websocket() {
|
||||||
this.connect_websocket().catch((e) => {
|
this.connect_websocket().catch((e) => {
|
||||||
console.error('Failed to initialize "${this.slug}"');
|
console.error(
|
||||||
throw e;
|
`Failed to connect to "${this.slug}", attempting reconnection in 5 seconds`
|
||||||
|
);
|
||||||
|
console.error(e);
|
||||||
|
setTimeout(() => this.try_connect_websocket(), 5000);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async connect_websocket() {
|
async connect_websocket() {
|
||||||
// initial setup
|
const settings = await this.api_get('settings');
|
||||||
if (!this.session_key) {
|
this.webcamURL = new URL(settings.webcam.streamUrl, this.address);
|
||||||
try {
|
this.name = settings.appearance.name;
|
||||||
const settings = await this.api_get('settings');
|
|
||||||
this.webcamURL = new URL(settings.webcam.streamUrl, this.address);
|
|
||||||
this.name = settings.appearance.name;
|
|
||||||
|
|
||||||
// do passive login to get a session key from the API key
|
// do passive login to get a session key from the API key
|
||||||
const login: octoprint.LoginResponse = await this.api_post('login', {
|
const login: octoprint.LoginResponse = await this.api_post('login', {
|
||||||
passive: 'true',
|
passive: 'true',
|
||||||
});
|
});
|
||||||
this.session_key = login.name + ':' + login.session;
|
const session_key = login.name + ':' + login.session;
|
||||||
} catch {
|
|
||||||
console.log(
|
let pingSender: ReturnType<typeof setInterval>;
|
||||||
`Failed to connect to "${this.slug}" attempting reconnection in 5 seconds`
|
let pongTimeout: Timeout;
|
||||||
);
|
|
||||||
setTimeout(() => this.connect_websocket(), 5000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = new URL('/sockjs/websocket', this.address);
|
const url = new URL('/sockjs/websocket', this.address);
|
||||||
url.protocol = 'ws';
|
url.protocol = 'ws';
|
||||||
this.websocket = new WebSocket(url.toString());
|
let websocket = new WebSocket(url.toString());
|
||||||
this.websocket
|
websocket
|
||||||
.on('open', () => {
|
.on('open', () => {
|
||||||
|
pingSender = setInterval(() => websocket.ping(), PING_TIME);
|
||||||
|
pongTimeout = this.heartbeat(websocket, pongTimeout);
|
||||||
|
|
||||||
console.log(`Connected to "${this.slug}"`);
|
console.log(`Connected to "${this.slug}"`);
|
||||||
this.websocket!.send(JSON.stringify({ auth: this.session_key }));
|
websocket.send(JSON.stringify({ auth: session_key }));
|
||||||
})
|
})
|
||||||
.on('message', (data: WebSocket.Data) => {
|
.on('message', (data: WebSocket.Data) => {
|
||||||
const event: octoprint.Message = JSON.parse(data as string);
|
const event: octoprint.Message = JSON.parse(data as string);
|
||||||
@ -130,14 +132,28 @@ class PrinterStatus {
|
|||||||
this.lastStatus = ext_event;
|
this.lastStatus = ext_event;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.on('pong', () => {
|
||||||
|
pongTimeout = this.heartbeat(websocket, pongTimeout);
|
||||||
|
})
|
||||||
.on('close', () => {
|
.on('close', () => {
|
||||||
|
clearInterval(pingSender);
|
||||||
|
clearTimeout(pongTimeout);
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
`Lost connection to "${this.slug}" attempting reconnection in 5 seconds`
|
`Lost connection to "${this.slug}", attempting reconnection in 5 seconds`
|
||||||
);
|
);
|
||||||
setTimeout(() => this.connect_websocket(), 5000);
|
setTimeout(() => this.try_connect_websocket(), 5000);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
heartbeat(websocket: WebSocket, pongTimeout: Timeout): Timeout {
|
||||||
|
clearTimeout(pongTimeout);
|
||||||
|
return setTimeout(() => {
|
||||||
|
console.log(`Missed 2 heartbeats for "${this.slug}", disconnecting`);
|
||||||
|
websocket.terminate();
|
||||||
|
}, PING_TIME * 2);
|
||||||
|
}
|
||||||
|
|
||||||
async api_get(endpoint: string): Promise<any> {
|
async api_get(endpoint: string): Promise<any> {
|
||||||
const r = await fetch(new URL('/api/' + endpoint, this.address), {
|
const r = await fetch(new URL('/api/' + endpoint, this.address), {
|
||||||
headers: { 'X-Api-Key': this.apikey },
|
headers: { 'X-Api-Key': this.apikey },
|
||||||
|
Loading…
Reference in New Issue
Block a user