import { Datagram } from "../../Independents/Datagram/datagram-types.js"; import { CDatagram, DatagramShort, isRequest } from "../../Independents/Datagram/datagram.js"; import { decodeMessage, encodeMessage } from "./EncodeDecodeMessage.js"; import { Transport } from "./Transport.js"; import { OutgoingPulse } from "../types.js"; import { timeStamp } from "console"; import { msToTime } from "../../Independents/util/misc.js"; export abstract class BinaryPacketTransport extends Transport { enableOutgoingHeartbeats = false; enableIncommingHeartbeats = false; sending: boolean = false; lastActivity = 0; closing: boolean = false; turnaroundClock = 0; supressDatagrams = 0; connected = false; transmitDatagram(d: Datagram) { //this.employer.gateway.state.log("Encode", DatagramShort(d)); if (isRequest(d)) { if (d.reqno < this.supressDatagrams) { //console.log("SUPRESSING SENDING DATAGRAM", d.reqno); return; } } let msg = encodeMessage(d); this.sendOutgoingPackage(msg); } outgoingPulse: OutgoingPulse = { interval: null, host: "", heartbeat: 0, inactive: 0 }; abstract sendOutgoingPackage(blob: ArrayBuffer): void; clearHeartbeat(resetCounters: boolean) { // let pulse = Burpanet.currentTenants2.pulses[this.__host]; if (this.outgoingPulse.interval) { clearTimeout(this.outgoingPulse.interval); this.outgoingPulse.interval = null; } if (resetCounters) { //console.log("reset heartbeat counters"); this.outgoingPulse.inactive = 0; this.outgoingPulse.heartbeat = 0; } } close() { this.clearHeartbeat(true); } get debug() { let ret = super.debug; if (this.enableOutgoingHeartbeats || this.enableIncommingHeartbeats) { ret.lastHeartbeat = `${msToTime(Date.now() - this.lastHeartbeat)} ago`; ret.firstHeartbeat = `${msToTime(Date.now() - this.firstHeartbeat)} ago` } return ret; } restartHeartbeat(resetCounters: boolean, immediate: boolean, resetFrequency: boolean) { if (!this.enableOutgoingHeartbeats) { return; } //console.log("RESTARTING HEARTBEAT"); this.clearHeartbeat(resetCounters); if (this.closing) { return; } //pulse = Burpanet.currentTenants2.pulses[host]; let heartbeatfn = () => { if (this.connected && !this.sending) { this.lastHeartbeat = Date.now(); this.outgoingPulse.heartbeat++; this.outgoingPulse.inactive++; // console.log("heartbeat from ", this.host, this.pulse) let bytes = encodeMessage({ heartbeat: this.outgoingPulse.heartbeat }); this.sendOutgoingPackage(bytes); } }; if (immediate) { // console.log("immediate"); // heartbeatfn(); } if (this.lastActivity == 0 || resetFrequency) { this.lastActivity = Date.now(); } /** * * @param t Send in a range between approximately -6 and +6 to get a curve between 0 and 1. * If you send in 0, you get 0.5. * @returns A number between 0 and 1. */ function sigmoid(t: number) { return 1 / (1 + Math.pow(Math.E, -t)); } let speedup = 1; // 60*60; let timeConstant = 1000; let sCurveLength = 1; let secondHalfsCurveLength = 100; let halfTime = 2000; let maxTime = 20000; let secondHalfAt = 10; let minFreq = 200; let now = Date.now(); let passed = (now - this.lastActivity) * speedup; let s = passed / (timeConstant * sCurveLength) - 6; let presentableS = Math.floor(s * 100) / 100; let frequency: number; let soBreak = sigmoid(secondHalfAt); // 0.6 // Heartbeat follows S-curve. High when traffic is high, then slowing down fast to about 30 hb/min and then slowing down slow to about 3 hb/min. if (s < secondHalfAt) { let so = sigmoid(s); frequency = Math.floor(minFreq + so * (halfTime * (1 / soBreak) - minFreq)); //console.log("slowing fast", "sigmoid input", presentableS, "sigmoid output", so, "frequency", frequency); this.turnaroundClock = Date.now();; } else { let passed2 = (now - this.turnaroundClock) * speedup; let normalized = 1 - (1 / ((passed2 / (200000 * secondHalfsCurveLength)) + 1)); frequency = Math.min(maxTime, halfTime + (normalized * (maxTime - halfTime))); //console.log("passed", `${(passed / (1000 * 60 * 60))}timmar`, "soBreak", soBreak, "normalized", normalized, "frequency", frequency); } this.outgoingPulse.interval = setTimeout(heartbeatfn, frequency); } onIncommingPackage(msg: ArrayBuffer) { //console.log("INCOMMING", msg); let responseMsg = decodeMessage(msg); //console.log("Received message on binary Transport", responseMsg); if (isHeartbeat(responseMsg)) { this.lastHeartbeat = Date.now() return; } let datagram = CDatagram.fromInit(responseMsg); //this.employer.gateway.state.log("Decode", DatagramShort(datagram)); this.onReceiveDatagram(datagram); // let generation = Number.parseInt(localStorage.getItem("burpa.generation")??"0"); // console.log("SERVER GENERATION IS ", responseMsg.body); // if (serverGeneration == -1) { // serverGeneration = responseMsg.body.generation; // } else if (serverGeneration !== responseMsg.body.generation) { // console.log("SERVER IS NEW GENERATION. PLEASE REFRESH."); // serverGeneration = responseMsg.body.generation; // if (typeof (window) !== 'undefined') { // // TODO! move somewhere else // window.location.href = window.location.href; // } else if ((self as any).registration) { // console.log("INSIDE SERVICE WORKER. REFRESH!", (self as any).registration) // let Self = (self as any); // console.log("PROMISE CLIENTS", Self.clients); // let clients = Self.clients.matchAll(); // clients.then((clients: any) => { // console.log("CLIENTS", clients); // clients.forEach((client: any) => { // console.log("NAVIGATE"); // // client.navigate(client.url); // }) // }); // } //} // if (generation !== responseMsg.body.generation) { // console.log("NEW GENERATION. PLEASE REFRESH"); // globalThis.localStorage.setItem("burpa.generation",(++generation).toString()); // } // This is a response to a host spawn. The host has been registred externally. //console.log("have registred host externally", r); let body = responseMsg.body; //hostEntry.host = msg.host; //this.__host = msg.host; //Burpanet.currentTenants[msg.host] = hostEntry; // restartHeartbeat(msg.host) this.restartHeartbeat(false, false, true); // let resp = new CResponse(r, new CRequest("/burpa/tenants", { body: { host: msg.host } })) // console.log("Have created host", r) //resolve(body); return; } } function isHeartbeat(msg: any): boolean { return msg.heartbeat !== undefined; }