// lib/core/process.ts // Copyright (C) 2025 DTP Technologies, LLC // All Rights Reserved import env from "../../config/env.js"; import os from "node:os"; import { CronJob } from "cron"; import diskusage from "diskusage"; import { DtpPlatform } from "./platform.js"; import { DtpComponent } from "./component.js"; import { IDtpProcessStats } from "../../app/models/lib/process-stats.js"; import SystemInformation from "systeminformation"; interface CpuTimes { /** The number of milliseconds the CPU has spent in user mode. */ user: number; /** The number of milliseconds the CPU has spent in nice mode. */ nice: number; /** The number of milliseconds the CPU has spent in sys mode. */ sys: number; /** The number of milliseconds the CPU has spent in idle mode. */ idle: number; /** The number of milliseconds the CPU has spent in irq mode. */ irq: number; } export abstract class DtpProcess extends DtpPlatform { maxCpuTime: number = 0; processStats: IDtpProcessStats = { cpu: 0, memory: 0, storage: 0, netBytesTx: 0, netBytesRx: 0, }; reportJob?: CronJob; abstract reportStats(): Promise; constructor(component: DtpComponent) { super(component); } async start(): Promise { await super.start(); this.reportJob = new CronJob( "0 * * * * *", // top of every minute this.reportStats.bind(this), null, true, env.timezone ); } async stop(): Promise { if (this.reportJob) { this.log.info("stopping host report job"); this.reportJob.stop(); delete this.reportJob; } return super.stop(); } async updateStats(): Promise { await this.getCpuUsage(); await this.getMemoryUsage(); await this.getDiskUsage(env.https.uploadPath); await this.getNetworkUsage(); } async getMemoryUsage() { const freeMem = os.freemem(); const totalMem = os.totalmem(); this.processStats.memory = freeMem / totalMem; } async getCpuUsage() { this.processStats.cpu = 0; const cpus = os.cpus(); if (cpus.length > 0) { const cpuTimes: CpuTimes = { idle: 0, irq: 0, nice: 0, sys: 0, user: 0, }; for (const cpu of cpus) { cpuTimes.idle += cpu.times.idle; cpuTimes.irq += cpu.times.irq; cpuTimes.nice += cpu.times.nice; cpuTimes.sys += cpu.times.sys; cpuTimes.user += cpu.times.user; } cpuTimes.idle /= cpus.length; cpuTimes.irq /= cpus.length; cpuTimes.nice /= cpus.length; cpuTimes.sys /= cpus.length; cpuTimes.user /= cpus.length; const cpuTime = cpuTimes.idle + cpuTimes.irq + cpuTimes.nice + cpuTimes.sys + cpuTimes.user; this.processStats.cpu = cpuTime / this.maxCpuTime; } } async getDiskUsage(pathname: string): Promise { return new Promise((resolve, reject) => { diskusage.check( pathname, (error?: Error, usage?: diskusage.DiskUsage) => { if (error) { return reject(error); } if (usage) { this.processStats.storage = usage.available / usage.total; return resolve(); } return 0; } ); }); } async getNetworkUsage(): Promise { this.processStats.netBytesRx = 0; this.processStats.netBytesTx = 0; const interfaces = await SystemInformation.networkStats("*"); for (const iface of interfaces) { this.processStats.netBytesTx += iface.tx_bytes; this.processStats.netBytesRx += iface.rx_bytes; } } }