UNPKG

modbus-connect

Version:

Modbus RTU over Web Serial and Node.js SerialPort

219 lines (174 loc) 7.18 kB
// utils/diagnostics.js class Diagnostics { constructor() { this.reset(); } reset() { this.startTime = Date.now(); this.totalRequests = 0; this.successfulResponses = 0; this.errorResponses = 0; this.timeouts = 0; this.crcErrors = 0; this.modbusExceptions = 0; this.totalRetries = 0; this.totalRetrySuccesses = 0; this.lastResponseTime = null; this._totalResponseTime = 0; this._totalResponseTimeAll = 0; this.lastErrorMessage = null; this.lastErrors = []; this.functionCallCounts = {}; this.errorMessageCounts = {}; this.lastSuccessDetails = null; this._lastFuncCode = null; this.lastExceptionCode = null; this.totalDataSent = 0; this.totalDataReceived = 0; this.lastRequestTimestamp = null; this.lastSuccessTimestamp = null; this.lastErrorTimestamp = null; this.totalSessions ??= 0; this.totalSessions++; } recordRequest() { this.totalRequests++; this.lastRequestTimestamp = new Date().toISOString(); } recordRetry(attempts) { this.totalRetries += attempts; } recordRetrySuccess() { this.totalRetrySuccesses++; } recordFunctionCall(funcCode) { if (funcCode == null) return; this._lastFuncCode = funcCode; this.functionCallCounts[funcCode] ??= 0; this.functionCallCounts[funcCode]++; } recordSuccess(responseTimeMs) { this.successfulResponses++; this.lastResponseTime = responseTimeMs; this._totalResponseTime += responseTimeMs; this._totalResponseTimeAll += responseTimeMs; this.lastSuccessTimestamp = new Date().toISOString(); this.lastSuccessDetails = { responseTime: responseTimeMs, timestamp: this.lastSuccessTimestamp, funcCode: this._lastFuncCode ?? null }; } recordError(error, { code = null, responseTimeMs = 0, exceptionCode = null } = {}) { this.errorResponses++; this.lastErrorMessage = error.message || String(error); this._totalResponseTimeAll += responseTimeMs; this.lastErrorTimestamp = new Date().toISOString(); this.lastErrors.push(this.lastErrorMessage); if (this.lastErrors.length > 10) { this.lastErrors.shift(); } const msg = (error.message || '').toLowerCase(); if (code === 'timeout' || msg.includes('timeout')) { this.timeouts++; } else if (code === 'crc' || msg.includes('crc')) { this.crcErrors++; } else if (code === 'modbus-exception' || msg.includes('modbus exception')) { this.modbusExceptions++; if (typeof exceptionCode === 'number') { this.lastExceptionCode = exceptionCode; } } this.errorMessageCounts[this.lastErrorMessage] ??= 0; this.errorMessageCounts[this.lastErrorMessage]++; } recordDataSent(byteLength) { this.totalDataSent += byteLength; } recordDataReceived(byteLength) { this.totalDataReceived += byteLength; } get averageResponseTime() { return this.successfulResponses === 0 ? null : this._totalResponseTime / this.successfulResponses; } get averageResponseTimeAll() { const total = this.successfulResponses + this.errorResponses; return total === 0 ? null : this._totalResponseTimeAll / total; } get errorRate() { return this.totalRequests === 0 ? null : (this.errorResponses / this.totalRequests) * 100; } get uptimeSeconds() { return Math.floor((Date.now() - this.startTime) / 1000); } getStats() { return { uptimeSeconds: this.uptimeSeconds, totalSessions: this.totalSessions, totalRequests: this.totalRequests, successfulResponses: this.successfulResponses, errorResponses: this.errorResponses, timeouts: this.timeouts, crcErrors: this.crcErrors, modbusExceptions: this.modbusExceptions, lastExceptionCode: this.lastExceptionCode, totalRetries: this.totalRetries, totalRetrySuccesses: this.totalRetrySuccesses, lastResponseTime: this.lastResponseTime, averageResponseTime: this.averageResponseTime, averageResponseTimeAll: this.averageResponseTimeAll, errorRate: this.errorRate, lastErrorMessage: this.lastErrorMessage, lastErrors: [...this.lastErrors], lastSuccessDetails: this.lastSuccessDetails, functionCallCounts: { ...this.functionCallCounts }, commonErrors: Object.entries(this.errorMessageCounts) .sort((a, b) => b[1] - a[1]) .slice(0, 3) .map(([message, count]) => ({ message, count })), dataSent: this.totalDataSent, dataReceived: this.totalDataReceived, lastRequestTimestamp: this.lastRequestTimestamp, lastSuccessTimestamp: this.lastSuccessTimestamp, lastErrorTimestamp: this.lastErrorTimestamp }; } serialize() { return JSON.stringify(this.getStats(), null, 2); } toTable() { const stats = this.getStats(); return Object.entries(stats).map(([metric, value]) => ({ metric, value })); } mergeWith(other) { if (!(other instanceof Diagnostics)) return; this.totalRequests += other.totalRequests; this.successfulResponses += other.successfulResponses; this.errorResponses += other.errorResponses; this.timeouts += other.timeouts; this.crcErrors += other.crcErrors; this.modbusExceptions += other.modbusExceptions; this.totalRetries += other.totalRetries; this.totalRetrySuccesses += other.totalRetrySuccesses; this._totalResponseTime += other._totalResponseTime; this._totalResponseTimeAll += other._totalResponseTimeAll; this.totalDataSent += other.totalDataSent; this.totalDataReceived += other.totalDataReceived; other.lastErrors.forEach(err => this.lastErrors.push(err)); if (this.lastErrors.length > 10) { this.lastErrors = this.lastErrors.slice(-10); } for (const [code, count] of Object.entries(other.functionCallCounts)) { this.functionCallCounts[code] ??= 0; this.functionCallCounts[code] += count; } for (const [msg, count] of Object.entries(other.errorMessageCounts)) { this.errorMessageCounts[msg] ??= 0; this.errorMessageCounts[msg] += count; } this.totalSessions += other.totalSessions ?? 0; } } module.exports = { Diagnostics }