blob: 88d71461295a705906107d464f4ec648c25c0474 [file] [log] [blame] [edit]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {getUTC, getRelTime, timeElapsed, second} from '@parallax/common/time';
import {parseMessages} from '@parallax/data/parse';
let client: StreamingClient|undefined;
/**
* Handles the connection with a streaming server.
*/
class StreamingClient {
/* Server network path. */
readonly server: string;
/* Interval between fetching data. */
readonly fetchInterval: second;
/* Maximum time disconnected from the server before disconnecting. */
readonly maxDisconnection: second;
/* Reference timestamp for disconnection. */
private timestamp: second;
/* Timer ID */
private timer: ReturnType<typeof setTimeout>|undefined;
/* Connected status. */
private connected = false;
/**
* Creates a new Server
* @param server Server path including port
*/
constructor(server: string) {
this.server = server;
this.fetchInterval = 0.1;
this.maxDisconnection = 10;
this.timestamp = getRelTime();
}
/**
* Reset the timer.
*/
async run() {
this.connected = true;
try {
// Attempt to fetch the data.
await this.getData();
this.timestamp = getRelTime();
} catch (error) {
console.error('Error', error);
}
if (timeElapsed(this.timestamp) <= this.maxDisconnection) {
this.resetTimer();
} else {
console.error('Disonnecting from client due to timeout.');
this.disconnect();
}
}
/**
* Reset the timer so we call run again.
*/
resetTimer() {
clearTimeout(this.timer);
this.timer = setTimeout(this.run.bind(this), this.fetchInterval * 1000);
}
/**
* Disconnect the client from the server.
*/
disconnect() {
clearTimeout(this.timer);
this.connected = false;
}
/**
* Send and receive objects between the server.
*
* Uses JSON and POST methods to serialize a message and submit it to
* the server. Deserializes the response from the server and returns it.
*
* @param sendData JSON serializable object passed to the server.
* @return Deserialized JSON response from the server.
*/
async transfer(sendData: any) {
const sendText = JSON.stringify(sendData);
const response = await fetch(this.server, {
method: 'POST',
mode: 'cors',
cache: 'no-cache',
headers: {'Content-Type': 'text/plain'},
body: sendText,
});
const text = await response.text();
return JSON.parse(text);
}
/**
* Request data from the server and pass the response to the callbacks.
* @param server Server address and port.
*/
async getData() {
let utc = getUTC();
const request = {
type: 'getData',
startTime: utc - 10,
};
const data = await this.transfer(request);
parseMessages(data);
}
/**
* Query if the client is currently connected to the server.
* @return True if it is connected.
*/
isConnected() {
return this.connected;
}
}
/**
* Query if any clients are connected to the server.
* @return True if any clients are connected.
*/
export function clientConnected() {
if (client) {
return client.isConnected();
}
return false;
}
/**
* Connect and disconnect the client from the streaming server.
* @param server Server path and port.
*/
export async function toggleClient(server: string) {
if (!clientConnected()) {
client = new StreamingClient(server);
client.run();
} else {
if (client) {
client.disconnect();
}
client = undefined;
}
}